Školení
Modul
Výběr správného datového typu v kódu jazyka C# - Training
Zvolte pro svůj kód správný datový typ z několika základních typů používaných v jazyce C#.
Tento prohlížeč se už nepodporuje.
Upgradujte na Microsoft Edge, abyste mohli využívat nejnovější funkce, aktualizace zabezpečení a technickou podporu.
Záznamy jsou typy, které používají rovnost na základě hodnot. C# 10 přidá struktury záznamů, abyste mohli definovat záznamy jako typy hodnot. Dvě proměnné typu záznamu jsou stejné, pokud jsou definice typu záznamu stejné a pokud jsou pro každé pole hodnoty v obou záznamech stejné. Dvě proměnné typu třídy jsou stejné, pokud jsou objekty odkazující na stejný typ třídy a proměnné odkazují na stejný objekt. Rovnost založená na hodnotách znamená další možnosti, které budete pravděpodobně chtít v typech záznamů. Kompilátor generuje mnoho z těchto členů, když deklarujete místo record
class
. Kompilátor generuje stejné metody pro record struct
typy.
V tomto kurzu se naučíte:
record
modifikátor class
.Budete muset nastavit počítač tak, aby běžel .NET 6 nebo novější, včetně kompilátoru C# 10 nebo novějšího. Kompilátor C# 10 je k dispozici od sady Visual Studio 2022 nebo sady .NET 6 SDK.
Záznam definujete deklarací typu pomocí klíčového record
slova, úpravou class
nebo struct
deklarace. Volitelně můžete vynechat class
klíčové slovo k vytvoření record class
. Záznam se řídí sémantikou rovnosti na základě hodnot. Kompilátor vynucuje sémantiku hodnot několika metod pro váš typ záznamu (pro record class
typy i record struct
typy):
Equals
metoda, jejíž parametr je typem záznamu.operator ==
a operator !=
.Záznamy také poskytují přepsání .Object.ToString() Kompilátor syntetizuje metody pro zobrazení záznamů pomocí Object.ToString(). Tyto členy prozkoumáte při psaní kódu pro tento kurz. Záznamy podporují with
výrazy, které umožňují nedestruktivní mutování záznamů.
Poziční záznamy můžete deklarovat také pomocí stručnější syntaxe. Kompilátor pro vás syntetizuje více metod při deklaraci pozičních záznamů:
record class
typy a readonly record struct
typy. U record struct
typů se jedná o čtení i zápis.Deconstruct
extrakce vlastností ze záznamu.Data a statistiky patří mezi scénáře, ve kterých chcete použít záznamy. V tomto kurzu vytvoříte aplikaci, která počítá dny stupňů pro různá použití. Dny stupňů jsou měřením tepla (nebo nedostatkem tepla) v období dnů, týdnů nebo měsíců. Dny stupňů sledují a předpovídají spotřebu energie. Čím více horké dny, znamená více klimatizace a chladnější dny znamenají více využití pece. Dny stupňů pomáhají spravovat populace rostlin a korelovat s růstem rostlin při změně ročních období. Dny stupňů pomáhají sledovat migrace zvířat pro druhy, které cestují tak, aby odpovídaly klimatu.
Vzorec je založen na střední teplotě v daném dni a základní teplotě. K výpočtu stupně dnů v čase budete potřebovat vysokou a nízkou teplotu každý den po určitou dobu. Začněme vytvořením nové aplikace. Vytvořte novou konzolovou aplikaci. Vytvořte nový typ záznamu v novém souboru s názvem "DailyTemperature.cs":
public readonly record struct DailyTemperature(double HighTemp, double LowTemp);
Předchozí kód definuje poziční záznam. Záznam DailyTemperature
je záznam readonly record struct
, protože nemáte v úmyslu zdědit z něj a měl by být neměnný. Vlastnosti HighTemp
jsou inicializační pouze vlastnosti, což znamená, že mohou být nastaveny v konstruktoru nebo pomocí inicializátoru LowTemp
vlastností. Pokud byste chtěli, aby poziční parametry byly pro čtení i zápis, deklarujete místo record struct
readonly record struct
. Typ DailyTemperature
má také primární konstruktor , který má dva parametry, které odpovídají dvěma vlastnostem. K inicializaci záznamu DailyTemperature
použijete primární konstruktor. Následující kód vytvoří a inicializuje několik DailyTemperature
záznamů. První používá pojmenované parametry k objasnění HighTemp
a LowTemp
. Zbývající inicializátory používají poziční parametry k inicializaci parametru HighTemp
a 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)
];
Do záznamů můžete přidat vlastní vlastnosti nebo metody, včetně pozičních záznamů. Budete muset vypočítat průměrnou teplotu pro každý den. Tuto vlastnost můžete přidat do záznamu DailyTemperature
:
public readonly record struct DailyTemperature(double HighTemp, double LowTemp)
{
public double Mean => (HighTemp + LowTemp) / 2.0;
}
Pojďme se ujistit, že tato data můžete použít. Do metody přidejte následující kód Main
:
foreach (var item in data)
Console.WriteLine(item);
Spusťte aplikaci a zobrazí se výstup podobný následujícímu zobrazení (několik řádků odebráno pro místo):
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 }
Předchozí kód ukazuje výstup z přepsání ToString
syntetizované kompilátorem. Pokud dáváte přednost jinému textu, můžete napsat vlastní verzi ToString
, která brání kompilátoru v syntetizaci verze za vás.
Pokud chcete vypočítat dny, vezmete rozdíl od základní teploty a střední teploty v daném dni. Pokud chcete měřit teplo v průběhu času, zahoďte všechny dny, ve kterých je střední teplota nižší než směrný plán. Pokud chcete měřit chlad v průběhu času, zahodíte všechny dny, ve kterých je střední teplota vyšší než směrný plán. Například USA používají jako základnu 65F jak pro vytápění, tak pro dny chladicího stupně. To je teplota, ve které není potřeba topení ani chlazení. Pokud má den průměrnou teplotu 70F, je tento den pět dní chlazení a nulový stupeň vytápění dny. Pokud je naopak průměrná teplota 55F, je tento den 10 dní ve stupních vytápění a 0 dnů chlazení.
Tyto vzorce můžete vyjádřit jako malou hierarchii typů záznamů: typ dne abstraktního stupně a dva konkrétní typy pro dny vytápění a dny stupně chlazení. Tyto typy mohou být také poziční záznamy. Jako argumenty primárního konstruktoru přebírají základní teplotu a posloupnost záznamů o denní teplotě:
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);
}
Abstraktní DegreeDays
záznam je sdílenou základní třídou pro HeatingDegreeDays
záznamy i CoolingDegreeDays
záznamy. Deklarace primárního konstruktoru na odvozených záznamech ukazují, jak spravovat inicializaci základního záznamu. Odvozený záznam deklaruje parametry pro všechny parametry v primárním konstruktoru základního záznamu. Základní záznam deklaruje a inicializuje tyto vlastnosti. Odvozený záznam je neskryje, ale vytvoří a inicializuje vlastnosti parametrů, které nejsou deklarovány v základním záznamu. V tomto příkladu odvozené záznamy nepřidají nové parametry primárního konstruktoru. Otestujte kód přidáním následujícího kódu do metody Main
:
var heatingDegreeDays = new HeatingDegreeDays(65, data);
Console.WriteLine(heatingDegreeDays);
var coolingDegreeDays = new CoolingDegreeDays(65, data);
Console.WriteLine(coolingDegreeDays);
Zobrazí se výstup podobný následujícímu zobrazení:
HeatingDegreeDays { BaseTemperature = 65, TempRecords = record_types.DailyTemperature[], DegreeDays = 85 }
CoolingDegreeDays { BaseTemperature = 65, TempRecords = record_types.DailyTemperature[], DegreeDays = 71.5 }
Kód vypočítá správný počet dnů ve stupních vytápění a chlazení v daném časovém období. Tento příklad ale ukazuje, proč můžete chtít nahradit některé syntetizované metody pro záznamy. Můžete deklarovat vlastní verzi libovolné z metod syntetizovaných kompilátorem v typu záznamu s výjimkou metody klonování. Metoda klonování má vygenerovaný název kompilátoru a nemůžete zadat jinou implementaci. Tyto syntetizované metody zahrnují konstruktor kopírování, členy System.IEquatable<T> rozhraní, rovnost a nerovnost testy a GetHashCode(). Za tímto účelem syntetizujete PrintMembers
. Můžete také deklarovat vlastní ToString
, ale PrintMembers
poskytuje lepší možnost pro scénáře dědičnosti. Pokud chcete poskytnout vlastní verzi syntetizované metody, musí podpis odpovídat syntetizované metodě.
Prvek TempRecords
ve výstupu konzoly není užitečný. Zobrazí typ, ale nic jiného. Toto chování můžete změnit poskytnutím vlastní implementace syntetizované PrintMembers
metody. Podpis závisí na modifikátorech použitých na record
deklaraci:
sealed
typ záznamu record struct
nebo a , podpis je private bool PrintMembers(StringBuilder builder);
sealed
a odvozuje se od object
něj (to znamená, že deklaruje základní záznam), podpis je protected virtual bool PrintMembers(StringBuilder builder);
sealed
a odvozuje se z jiného záznamu, podpis je protected override bool PrintMembers(StringBuilder builder);
Tato pravidla jsou nejsnadnější pochopit pochopením účelu PrintMembers
. PrintMembers
přidá informace o každé vlastnosti v typu záznamu do řetězce. Smlouva vyžaduje, aby základní záznamy přidaly své členy do zobrazení a předpokládá, že odvozené členy přidají jejich členy. Každý typ záznamu ToString
syntetizuje přepsání, které vypadá podobně jako v následujícím příkladu:HeatingDegreeDays
public override string ToString()
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append("HeatingDegreeDays");
stringBuilder.Append(" { ");
if (PrintMembers(stringBuilder))
{
stringBuilder.Append(" ");
}
stringBuilder.Append("}");
return stringBuilder.ToString();
}
Deklarujete metodu PrintMembers
v záznamu DegreeDays
, který nevytiskne typ kolekce:
protected virtual bool PrintMembers(StringBuilder stringBuilder)
{
stringBuilder.Append($"BaseTemperature = {BaseTemperature}");
return true;
}
Podpis deklaruje metodu virtual protected
odpovídající verzi kompilátoru. Nemějte obavy, pokud dostanete přístupové objekty špatně; jazyk vynucuje správný podpis. Pokud zapomenete správné modifikátory pro libovolnou syntetizovanou metodu, kompilátor vydá upozornění nebo chyby, které vám pomůžou získat správný podpis.
V jazyce C# 10 a novějším můžete deklarovat metodu ToString
jako sealed
v typu záznamu. Tím se zabrání, aby odvozené záznamy poskytovaly novou implementaci. Odvozené záznamy budou stále obsahovat přepsání PrintMembers
. Pokud nechcete, aby zobrazoval typ modulu runtime záznamu, zapečetěli ToString
byste ho. V předchozím příkladu byste ztratili informace o tom, kde záznam měří dobu vytápění nebo chladicího stupně.
Syntetizované členy třídy pozičních záznamů nemění stav záznamu. Cílem je, abyste snadněji vytvářeli neměnné záznamy. Nezapomeňte, že deklarujete, readonly record struct
že vytvoříte neměnnou strukturu záznamu. Znovu se podívejte na předchozí deklarace pro HeatingDegreeDays
a CoolingDegreeDays
. Přidaní členové provádějí výpočty s hodnotami záznamu, ale neztlumují stav. Poziční záznamy usnadňují vytváření neměnných referenčních typů.
Vytváření neměnných referenčních typů znamená, že budete chtít použít nedestruktivní mutaci. Vytvoříte nové instance záznamů, které se podobají existujícím instancím záznamů pomocí with
výrazů. Tyto výrazy jsou konstrukce kopírování s dalšími přiřazeními, která upravují kopii. Výsledkem je nová instance záznamu, kde každá vlastnost byla zkopírována z existujícího záznamu a volitelně změněna. Původní záznam se nezmění.
Pojďme do programu přidat několik funkcí, které demonstrují with
výrazy. Nejprve vytvoříme nový záznam pro výpočet rostoucího stupně dnů pomocí stejných dat. Rostoucí stupeň dnů obvykle používá 41F jako směrný plán a měří teploty nad směrný plán. Pokud chcete použít stejná data, můžete vytvořit nový záznam, který se podobá coolingDegreeDays
, ale s jinou základní teplotou:
// Growing degree days measure warming to determine plant growing rates
var growingDegreeDays = coolingDegreeDays with { BaseTemperature = 41 };
Console.WriteLine(growingDegreeDays);
Můžete porovnat počet stupňů vypočítaných s čísly vygenerovanými s vyšší základní teplotou. Mějte na paměti, že záznamy jsou odkazové typy a tyto kopie jsou mělké kopie. Pole dat se nezkopíruje, ale oba záznamy odkazují na stejná data. Tato skutečnost je výhodou v jednom jiném scénáři. U rostoucích dnů stupňů je užitečné sledovat celkový součet za posledních pět dnů. Pomocí výrazů můžete vytvářet nové záznamy s různými zdrojovými daty with
. Následující kód sestaví kolekci těchto akumulace a pak zobrazí hodnoty:
// 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);
}
Pomocí výrazů můžete také with
vytvářet kopie záznamů. Nezadávejte žádné vlastnosti mezi složenými závorkami výrazu with
. To znamená, že vytvoříte kopii a nezměníte žádné vlastnosti:
var growingDegreeDaysCopy = growingDegreeDays with { };
Spuštěním dokončené aplikace zobrazte výsledky.
Tento kurz ukázal několik aspektů záznamů. Záznamy poskytují stručnou syntaxi pro typy, ve kterých se základní použití ukládá data. Pro objektově orientované třídy je základním použitím definování odpovědností. Tento kurz se zaměřuje na poziční záznamy, kde můžete pomocí stručné syntaxe deklarovat vlastnosti záznamu. Kompilátor syntetizuje několik členů záznamu pro kopírování a porovnávání záznamů. Pro typy záznamů můžete přidat další členy, které potřebujete. Neměnné typy záznamů můžete vytvořit s vědomím, že žádný z členů vygenerovaných kompilátorem neztlumí stav. A with
výrazy usnadňují podporu nedestruktivního mutování.
Záznamy přidávají další způsob, jak definovat typy. Definice slouží class
k vytváření hierarchií orientovaných na objekty, které se zaměřují na zodpovědnosti a chování objektů. Vytváříte struct
typy pro datové struktury, které ukládají data a jsou dostatečně malé, aby bylo kopírování efektivní. Typy vytvoříte record
, když chcete rovnost a porovnání založené na hodnotách, nechcete kopírovat hodnoty a chcete použít referenční proměnné. Typy vytváříte record struct
, pokud chcete, aby funkce záznamů pro typ, který je dostatečně malý k efektivnímu kopírování.
Další informace o záznamech najdete v referenčním článku jazyka C# pro typ záznamu a specifikaci navrhovaného typu záznamu a specifikaci struktury záznamů.
Zpětná vazba k produktu .NET
.NET je open source projekt. Vyberte odkaz pro poskytnutí zpětné vazby:
Školení
Modul
Výběr správného datového typu v kódu jazyka C# - Training
Zvolte pro svůj kód správný datový typ z několika základních typů používaných v jazyce C#.