Välja mellan anonyma typer och tupppeltyper
Att välja lämplig typ innebär att överväga dess användbarhet, prestanda och kompromisser jämfört med andra typer. Anonyma typer har varit tillgängliga sedan C# 3.0, medan generiska System.Tuple<T1,T2> typer introducerades med .NET Framework 4.0. Sedan dess har nya alternativ introducerats med stöd på språknivå, till exempel System.ValueTuple<T1,T2> – vilket som namnet antyder ger en värdetyp med flexibiliteten hos anonyma typer. I den här artikeln får du lära dig när det är lämpligt att välja en typ framför den andra.
Användbarhet och funktioner
Anonyma typer introducerades i C# 3.0 med LINQ-uttryck (Language-Integrated Query). Med LINQ projicerar utvecklare ofta resultat från frågor till anonyma typer som innehåller några utvalda egenskaper från de objekt som de arbetar med. Tänk på följande exempel som instansierar en matris med DateTime objekt och itererar genom att de projiceras till en anonym typ med två egenskaper.
var dates = new[]
{
DateTime.UtcNow.AddHours(-1),
DateTime.UtcNow,
DateTime.UtcNow.AddHours(1),
};
foreach (var anonymous in
dates.Select(
date => new { Formatted = $"{date:MMM dd, yyyy hh:mm zzz}", date.Ticks }))
{
Console.WriteLine($"Ticks: {anonymous.Ticks}, formatted: {anonymous.Formatted}");
}
Anonyma typer instansieras med hjälp av operatorn new
och egenskapsnamnen och typerna härleds från deklarationen. Om två eller flera anonyma objektinitierare i samma sammansättning anger en sekvens med egenskaper som är i samma ordning och som har samma namn och typer behandlar kompilatorn objekten som instanser av samma typ. De delar samma kompilatorgenererade typinformation.
Det tidigare C#-kodfragmentet projicerar en anonym typ med två egenskaper, ungefär som följande kompilatorgenererade C#-klass:
internal sealed class f__AnonymousType0
{
public string Formatted { get; }
public long Ticks { get; }
public f__AnonymousType0(string formatted, long ticks)
{
Formatted = formatted;
Ticks = ticks;
}
}
Mer information finns i anonyma typer. Samma funktioner finns med tupplar när du projicerar i LINQ-frågor, du kan välja egenskaper i tupplar. Dessa tupplar flödar genom frågan, precis som anonyma typer skulle göra. Överväg nu följande exempel med hjälp av System.Tuple<string, long>
.
var dates = new[]
{
DateTime.UtcNow.AddHours(-1),
DateTime.UtcNow,
DateTime.UtcNow.AddHours(1),
};
foreach (var tuple in
dates.Select(
date => new Tuple<string, long>($"{date:MMM dd, yyyy hh:mm zzz}", date.Ticks)))
{
Console.WriteLine($"Ticks: {tuple.Item2}, formatted: {tuple.Item1}");
}
System.Tuple<T1,T2>Med , exponerar instansen numrerade objektegenskaper, till exempel Item1
, och Item2
. Dessa egenskapsnamn kan göra det svårare att förstå avsikten med egenskapsvärdena, eftersom egenskapsnamnet endast tillhandahåller ordningstalet. Dessutom är typerna System.Tuple
referenstyper class
. Är System.ValueTuple<T1,T2> dock en värdetyp struct
. Följande C#-kodfragment används ValueTuple<string, long>
för att projicera till. På så sätt tilldelas den med hjälp av en literalsyntax.
var dates = new[]
{
DateTime.UtcNow.AddHours(-1),
DateTime.UtcNow,
DateTime.UtcNow.AddHours(1),
};
foreach (var (formatted, ticks) in
dates.Select(
date => (Formatted: $"{date:MMM dd, yyyy at hh:mm zzz}", date.Ticks)))
{
Console.WriteLine($"Ticks: {ticks}, formatted: {formatted}");
}
Mer information om tupplar finns i Tupplar (C#-referens) eller tupplar (Visual Basic).
De tidigare exemplen är alla funktionellt likvärdiga, men det finns små skillnader i deras användbarhet och deras underliggande implementeringar.
Avvägningar
Du kanske alltid vill använda ValueTuple över Tuple, och anonyma typer, men det finns kompromisser som du bör överväga. Typerna ValueTuple är föränderliga, medan Tuple de är skrivskyddade. Anonyma typer kan användas i uttrycksträd, medan tupplar inte kan göra det. Följande tabell är en översikt över några av de viktigaste skillnaderna.
Viktiga skillnader
Name | Åtkomstmodifierare | Typ | Anpassat medlemsnamn | Stöd för dekonstruktion | Stöd för uttrycksträd |
---|---|---|---|---|---|
Anonyma typer | internal |
class |
✔️ | ❌ | ✔️ |
Tuple | public |
class |
❌ | ❌ | ✔️ |
ValueTuple | public |
struct |
✔️ | ✔️ | ❌ |
Serialization
En viktig faktor när du väljer en typ är om den måste serialiseras eller inte. Serialisering är processen att konvertera tillståndet för ett objekt till ett formulär som kan bevaras eller transporteras. Mer information finns i serialisering. När serialisering är viktigt bör du skapa eller class
struct
föredras framför anonyma typer eller tuppelns typer.
Prestanda
Prestanda mellan dessa typer beror på scenariot. Den största effekten är kompromissen mellan allokeringar och kopiering. I de flesta scenarier är effekten liten. När större påverkan kan uppstå bör mätningar vidtas för att informera beslutet.
Slutsats
Som utvecklare som väljer mellan tupplar och anonyma typer finns det flera faktorer att tänka på. Om du i allmänhet inte arbetar med uttrycksträd och du är bekväm med tuppelns syntax väljer ValueTuple du när de ger en värdetyp med flexibiliteten att namnge egenskaper. Om du arbetar med uttrycksträd och föredrar att namnge egenskaper väljer du anonyma typer. Annars använder du Tuple.