Megosztás a következőn keresztül:


Rekordtípusok létrehozása

A rekordok értékalapú egyenlőséget használó típusok. A C# 10 rekordstruktúrákat ad hozzá, hogy a rekordokat értéktípusként definiálhassa. Egy rekordtípus két változója egyenlő, ha a rekordtípus-definíciók azonosak, és ha minden mező esetében a két rekord értéke egyenlő. Egy osztálytípus két változója egyenlő, ha a hivatkozott objektumok azonos osztálytípusúak, és a változók ugyanarra az objektumra hivatkoznak. Az értékalapú egyenlőség más képességeket is magában foglal, amelyeket valószínűleg érdemes használni a rekordtípusokban. A fordító sok ilyen tagot hoz létre, amikor egy helyett deklarál egy recordclass. A fordító ugyanazokat a metódusokat hozza létre a típusok esetében record struct .

Az oktatóanyag segítségével megtanulhatja a következőket:

  • Döntse el, hogy hozzáadja-e a record módosítót egy class típushoz.
  • Deklarálja a rekordtípusokat és a pozíciórekordtípusokat.
  • Helyettesítse a metódusokat a rekordokban létrehozott fordító metódusokkal.

Előfeltételek

Be kell állítania a gépet a .NET 6 vagy újabb verziójának futtatására, beleértve a C# 10 vagy újabb fordítót is. A C# 10 fordító a Visual Studio 2022-től vagy a .NET 6 SDK-tól kezdve érhető el.

A rekordok jellemzői

A rekordot úgy határozhatja meg, hogy deklarál egy típust a record kulcsszóval, módosít egy vagy struct deklarációtclass. Ha szeretné, kihagyhatja a class kulcsszót egy record class. Egy rekord az értékalapú egyenlőség szemantikáját követi. Az értékszemantika kényszerítéséhez a fordító számos metódust hoz létre a rekordtípushoz (mind a típusokrecord struct, mind a típusok esetébenrecord class):

A rekordok a felülbírálást is biztosítják Object.ToString(). A fordító a rekordok megjelenítésének módszereit szintetizálja a következő használatával Object.ToString(): . Az oktatóanyag kódjának megírása során megismerheti ezeket a tagokat. A rekordok támogatják with a rekordok roncsolásmentes mutációját lehetővé tevő kifejezéseket.

A pozíciórekordokat tömörebb szintaxissal is deklarálhatja. A fordító több metódust szintetizál a pozíciórekordok deklarálásakor:

  • Elsődleges konstruktor, amelynek paraméterei megegyeznek a rekorddeklarációban szereplő pozícióparaméterekkel.
  • Nyilvános tulajdonságok egy elsődleges konstruktor minden paraméteréhez. Ezek a tulajdonságok csak típusokhoz és readonly record struct típusokhoz record class használhatók. A típusok írási-olvasási record structműveletek.
  • A Deconstruct rekord tulajdonságainak kinyerésére használt módszer.

Hőmérsékletadatok összeállítása

Az adatok és statisztikák azon forgatókönyvek közé tartoznak, amelyekben rekordokat szeretne használni. Ebben az oktatóanyagban egy olyan alkalmazást fog létrehozni, amely különböző felhasználási módokon számítja ki a napok fokát. A foknapok a hő mértéke (vagy a hő hiánya) napok, hetek vagy hónapok alatt. A napok száma nyomon követi és előrejelzi az energiafelhasználást. A melegebb napok több légkondicionálót, a hidegebb napok pedig több kemencét használnak. A foknapok segítenek a növénypopulációk kezelésében, és korrelálnak a növények növekedésével, ahogy az évszakok változnak. A foknapok segítenek nyomon követni az éghajlathoz igazodó fajok állatmigrálását.

A képlet az adott napon mért középhőmérsékleten és az alaphőmérsékleten alapul. A napok időbeli kiszámításához minden nap magas és alacsony hőmérsékletre lesz szüksége egy adott ideig. Kezdjük egy új alkalmazás létrehozásával. Hozzon létre egy új konzolalkalmazást. Hozzon létre egy új rekordtípust egy "DailyTemperature.cs" nevű új fájlban:

public readonly record struct DailyTemperature(double HighTemp, double LowTemp);

Az előző kód egy pozíciórekordot határoz meg. A DailyTemperature rekord egy readonly record struct, mert nem kíván örökölni tőle, és nem módosítható. A HighTemp tulajdonságok csak LowTemp inicializálási tulajdonságok, ami azt jelenti, hogy a konstruktorban vagy egy tulajdonság inicializáló használatával állíthatók be. Ha azt szeretné, hogy a pozícióparaméterek írásvédettek legyenek, deklaráljon egy record structreadonly record struct. A DailyTemperature típus egy elsődleges konstruktort is tartalmaz, amely két paraméterrel rendelkezik, amelyek megfelelnek a két tulajdonságnak. Az elsődleges konstruktor használatával inicializálhat egy rekordot DailyTemperature . Az alábbi kód több DailyTemperature rekordot hoz létre és inicializál. Az első névvel ellátott paraméterekkel tisztázza az és LowTempa HighTemp . A többi inicializáló pozícióparaméterekkel inicializálja a HighTemp következőt LowTemp:

private static DailyTemperature[] data = [
    new DailyTemperature(HighTemp: 57, LowTemp: 30), 
    new DailyTemperature(60, 35),
    new DailyTemperature(63, 33),
    new DailyTemperature(68, 29),
    new DailyTemperature(72, 47),
    new DailyTemperature(75, 55),
    new DailyTemperature(77, 55),
    new DailyTemperature(72, 58),
    new DailyTemperature(70, 47),
    new DailyTemperature(77, 59),
    new DailyTemperature(85, 65),
    new DailyTemperature(87, 65),
    new DailyTemperature(85, 72),
    new DailyTemperature(83, 68),
    new DailyTemperature(77, 65),
    new DailyTemperature(72, 58),
    new DailyTemperature(77, 55),
    new DailyTemperature(76, 53),
    new DailyTemperature(80, 60),
    new DailyTemperature(85, 66) 
];

Saját tulajdonságokat vagy metódusokat adhat hozzá a rekordokhoz, beleértve a pozíciórekordokat is. Minden napra ki kell számítania a középhőmérsékletet. Ezt a tulajdonságot hozzáadhatja a DailyTemperature rekordhoz:

public readonly record struct DailyTemperature(double HighTemp, double LowTemp)
{
    public double Mean => (HighTemp + LowTemp) / 2.0;
}

Győződjön meg arról, hogy használhatja ezeket az adatokat. Adja hozzá a következő kódot a Main metódushoz:

foreach (var item in data)
    Console.WriteLine(item);

Futtassa az alkalmazást, és az alábbi megjelenítéshez hasonló kimenet jelenik meg (több sor el van távolítva a szóközhöz):

DailyTemperature { HighTemp = 57, LowTemp = 30, Mean = 43.5 }
DailyTemperature { HighTemp = 60, LowTemp = 35, Mean = 47.5 }


DailyTemperature { HighTemp = 80, LowTemp = 60, Mean = 70 }
DailyTemperature { HighTemp = 85, LowTemp = 66, Mean = 75.5 }

Az előző kód a fordító által szintetizált felülbírálás ToString kimenetét mutatja. Ha más szöveget szeretne, megírhatja a saját verzióját ToString , amely megakadályozza, hogy a fordító szintetizálja a verziót.

Számítási fok napjai

A napok kiszámításához az alaphőmérséklet és az adott napon mért középhőmérséklet közötti különbséget kell figyelembe vennie. A hő időalapú méréséhez minden olyan napot elvet, amikor a középhőmérséklet az alapérték alatt van. Az idő hidegének méréséhez minden olyan napot elvet, amikor a középhőmérséklet meghaladja az alaptervet. Az Egyesült Államok például 65F-et használ a fűtési és hűtési fok napjainak alapjaként. Ez az a hőmérséklet, ahol nincs szükség fűtésre vagy hűtésre. Ha egy nap középhőmérséklete 70F, akkor ez a nap öt hűtési nap és nulla fűtési fok nap. Ezzel szemben, ha a középhőmérséklet 55F, akkor az a nap 10 fűtési fok nap és 0 hűtési fok nap.

Ezeket a képleteket a rekordtípusok kis hierarchiájaként fejezheti ki: egy absztrakt fok naptípust és két konkrét típust a fűtési fok napjaihoz és a hűtési fok napjaihoz. Ezek a típusok pozíciórekordok is lehetnek. Az alaphőmérsékletet és a napi hőmérsékleti rekordok sorozatát veszik fel argumentumként az elsődleges konstruktor számára:

public abstract record DegreeDays(double BaseTemperature, IEnumerable<DailyTemperature> TempRecords);

public sealed record HeatingDegreeDays(double BaseTemperature, IEnumerable<DailyTemperature> TempRecords)
    : DegreeDays(BaseTemperature, TempRecords)
{
    public double DegreeDays => TempRecords.Where(s => s.Mean < BaseTemperature).Sum(s => BaseTemperature - s.Mean);
}

public sealed record CoolingDegreeDays(double BaseTemperature, IEnumerable<DailyTemperature> TempRecords)
    : DegreeDays(BaseTemperature, TempRecords)
{
    public double DegreeDays => TempRecords.Where(s => s.Mean > BaseTemperature).Sum(s => s.Mean - BaseTemperature);
}

Az absztrakt DegreeDays rekord a közös alaposztály mind a rekordokhoz, mind a HeatingDegreeDaysCoolingDegreeDays rekordokhoz. A származtatott rekordok elsődleges konstruktor-deklarációi bemutatják az alaprekord inicializálásának kezelését. A származtatott rekord az alaprekord elsődleges konstruktorának összes paraméteréhez deklarálja a paramétereket. Az alaprekord deklarálja és inicializálja ezeket a tulajdonságokat. A származtatott rekord nem rejti el őket, csak az alaprekordban nem deklarált paraméterek tulajdonságait hozza létre és inicializálja. Ebben a példában a származtatott rekordok nem adnak hozzá új elsődleges konstruktorparamétereket. A kód teszteléséhez adja hozzá a következő kódot a Main metódushoz:

var heatingDegreeDays = new HeatingDegreeDays(65, data);
Console.WriteLine(heatingDegreeDays);

var coolingDegreeDays = new CoolingDegreeDays(65, data);
Console.WriteLine(coolingDegreeDays);

A kimenet az alábbihoz hasonlóan jelenik meg:

HeatingDegreeDays { BaseTemperature = 65, TempRecords = record_types.DailyTemperature[], DegreeDays = 85 }
CoolingDegreeDays { BaseTemperature = 65, TempRecords = record_types.DailyTemperature[], DegreeDays = 71.5 }

Fordító-szintetizált metódusok definiálása

A kód kiszámítja a fűtési és hűtési fok napjainak megfelelő számát az adott időszakban. Ez a példa azonban azt mutatja be, hogy miért érdemes lecserélni néhány szintetizált metódust a rekordokra. A fordító által szintetizált metódusok bármelyikének saját verzióját rekordtípusban deklarálhatja a klónozási módszer kivételével. A klónozási módszer fordító által generált névvel rendelkezik, és nem adhat meg másik implementációt. Ezek a szintetizált módszerek közé tartozik a másolási konstruktor, a felület tagjai, az System.IEquatable<T> egyenlőségi és egyenlőtlenségi tesztek, és GetHashCode(). Erre a célra szintetizálni PrintMembersfog. Deklarálhatja a sajátját ToStringis, de PrintMembers jobb lehetőséget kínál az öröklési forgatókönyvekhez. A szintetizált metódus saját verziójának megadásához az aláírásnak meg kell egyeznie a szintetizált metódussal.

A TempRecords konzol kimenetének eleme nem hasznos. Megjeleníti a típust, de semmi mást. Ezt a viselkedést a szintetizált PrintMembers metódus saját implementációjának biztosításával módosíthatja. Az aláírás a deklarációra record alkalmazott módosítóktól függ:

  • Ha egy rekordtípus sealedvagy egy record structrekordtípus, akkor az aláírás private bool PrintMembers(StringBuilder builder);
  • Ha egy rekordtípus nem sealed , és származik object (vagyis nem deklarál egy alaprekordot), az aláírás protected virtual bool PrintMembers(StringBuilder builder);
  • Ha egy rekordtípus nem sealed egy másik rekordból származik, az aláírás protected override bool PrintMembers(StringBuilder builder);

Ezek a szabályok a legegyszerűbben a cél megértésén keresztül érthetők PrintMembers. PrintMembers Egy rekordtípus minden tulajdonságára vonatkozó információkat ad hozzá egy sztringhez. A szerződés megköveteli, hogy az alaprekordok hozzáadják a tagokat a megjelenítéshez, és feltételezik, hogy a származtatott tagok hozzáadják a tagokat. Minden rekordtípus egy ToString felülbírálást szintetizál, amely az alábbi példához HeatingDegreeDayshasonlóan néz ki:

public override string ToString()
{
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.Append("HeatingDegreeDays");
    stringBuilder.Append(" { ");
    if (PrintMembers(stringBuilder))
    {
        stringBuilder.Append(" ");
    }
    stringBuilder.Append("}");
    return stringBuilder.ToString();
}

Deklarál egy olyan metódust PrintMembers a DegreeDays rekordban, amely nem nyomtatja ki a gyűjtemény típusát:

protected virtual bool PrintMembers(StringBuilder stringBuilder)
{
    stringBuilder.Append($"BaseTemperature = {BaseTemperature}");
    return true;
}

Az aláírás a fordító verziójának megfelelő metódust virtual protected deklarál. Ne aggódjon, ha rosszul kapja meg a tartozékokat; a nyelv kikényszeríti a megfelelő aláírást. Ha elfelejti a szintetizált metódusok megfelelő módosítóit, a fordító figyelmeztetéseket vagy hibákat ad ki, amelyek segítenek a megfelelő aláírás beszerzésében.

A C# 10-es és újabb verzióiban a ToString metódust rekordtípusként sealed deklarálhatja. Ez megakadályozza, hogy a származtatott rekordok új implementációt biztosítsanak. A származtatott rekordok továbbra is tartalmazzák a felülbírálást PrintMembers . Ha nem szeretné, hogy megjelenjen a rekord futtatókörnyezetének típusa, lepecsételné ToString a zárolást. Az előző példában elveszítené az adatokat arról, hogy a rekord hol mérte a fűtési vagy hűtési fok napjait.

Roncsolásmentes mutáció

A pozíciórekordosztály szintetizált tagjai nem módosítják a rekord állapotát. A cél az, hogy könnyebben hozzon létre nem módosítható rekordokat. Ne feledje, hogy deklarál egy readonly record struct nem módosítható rekordstruktúra létrehozásához. Tekintse meg újra az előző deklarációkat a következőhöz HeatingDegreeDays : és CoolingDegreeDays. A hozzáadott tagok számításokat végeznek a rekord értékein, de nem mutálják az állapotot. A helymeghatározó rekordok megkönnyítik a nem módosítható referenciatípusok létrehozását.

A nem módosítható referenciatípusok létrehozása azt jelenti, hogy nem destruktív mutációt kell használnia. Kifejezések használatával with új rekordpéldányokat hozhat létre, amelyek hasonlóak a meglévő rekordpéldányokhoz. Ezek a kifejezések másolási konstrukciók, amelyek további hozzárendelésekkel módosítják a másolatot. Az eredmény egy új rekordpéldány, amelyben minden tulajdonság ki lett másolva a meglévő rekordból, és opcionálisan módosult. Az eredeti rekord változatlan.

Adjunk hozzá néhány olyan funkciót a programhoz, amelyek kifejezéseket mutatnak with be. Először hozzunk létre egy új rekordot a növekvő fokok napjainak kiszámításához ugyanazokkal az adatokkal. A növekedési fok napjai általában 41F-et használnak alapkonfigurációként, és az alapkonfiguráció feletti hőmérsékletet mérik. Ha ugyanazokat az adatokat szeretné használni, létrehozhat egy új rekordot, amely hasonló a coolingDegreeDays, de eltérő alaphőmérséklettel:

// Growing degree days measure warming to determine plant growing rates
var growingDegreeDays = coolingDegreeDays with { BaseTemperature = 41 };
Console.WriteLine(growingDegreeDays);

Összehasonlíthatja a kiszámított fokok számát a magasabb alaphőmérséklettel létrehozott számokkal. Ne feledje, hogy a rekordok referenciatípusok , és ezek a másolatok sekély másolatok. Az adatok tömbje nem másolódik, de mindkét rekord ugyanarra az adatra hivatkozik. Ez a tény előnyt jelent egy másik forgatókönyvben. A növekvő diplomás napok esetében hasznos nyomon követni az előző öt nap összesített adatait. Kifejezésekkel létrehozhat új rekordokat különböző forrásadatokkal with . Az alábbi kód összeállítja a felhalmozások gyűjteményét, majd megjeleníti az értékeket:

// showing moving accumulation of 5 days using range syntax
List<CoolingDegreeDays> movingAccumulation = new();
int rangeSize = (data.Length > 5) ? 5 : data.Length;
for (int start = 0; start < data.Length - rangeSize; start++)
{
    var fiveDayTotal = growingDegreeDays with { TempRecords = data[start..(start + rangeSize)] };
    movingAccumulation.Add(fiveDayTotal);
}
Console.WriteLine();
Console.WriteLine("Total degree days in the last five days");
foreach(var item in movingAccumulation)
{
    Console.WriteLine(item);
}

Kifejezésekkel rekordmásolatokat is with létrehozhat. Ne adjon meg tulajdonságokat a kifejezés zárójelei with között. Ez azt jelenti, hogy hozzon létre egy másolatot, és ne módosítsa a tulajdonságokat:

var growingDegreeDaysCopy = growingDegreeDays with { };

Futtassa a kész alkalmazást az eredmények megtekintéséhez.

Összesítés

Ez az oktatóanyag a rekordok számos aspektusát mutatta be. A rekordok tömör szintaxist biztosítanak az olyan típusokhoz, ahol az alapvető használat az adatok tárolása. Az objektumorientált osztályok esetében az alapvető használat a felelősségek meghatározása. Ez az oktatóanyag a pozíciórekordokra összpontosított, ahol tömör szintaxissal deklarálhatja egy rekord tulajdonságait. A fordító a rekord több tagját szintetizálja a rekordok másolásához és összehasonlításához. A rekordtípusokhoz további tagokat is hozzáadhat. Nem módosítható rekordtípusokat hozhat létre, tudva, hogy a fordító által létrehozott tagok egyike sem mutálná az állapotot. A with kifejezések megkönnyítik a roncsolásmentes mutációt.

A rekordok egy másik módszert is hozzáadnak a típusok definiálásához. class Definíciókkal objektumorientált hierarchiákat hozhat létre, amelyek az objektumok felelősségére és viselkedésére összpontosítanak. Olyan adatstruktúrákhoz hozhat létre struct típusokat, amelyek adatokat tárolnak, és elég kicsik a hatékony másoláshoz. record Ha értékalapú egyenlőséget és összehasonlítást szeretne, nem szeretne értékeket másolni, és referenciaváltozókat szeretne használni. Típusok akkor hozhatók létre record struct , ha egy olyan típus rekordjainak funkcióit szeretné használni, amelyek elég kicsik a hatékony másoláshoz.

A rekordokról a C# nyelvi referenciacikkében tájékozódhat a rekordtípusról, a javasolt rekordtípus-specifikációról és rekordstruktúra-specifikációról.