Wybieranie między typami anonimowych i krotki
Wybór odpowiedniego typu obejmuje uwzględnienie jego użyteczności, wydajności i kompromisów w porównaniu z innymi typami. Typy anonimowe są dostępne od wersji C# 3.0, podczas gdy typy ogólne System.Tuple<T1,T2> zostały wprowadzone w programie .NET Framework 4.0. Od tego czasu wprowadzono nowe opcje z obsługą poziomu języka, na System.ValueTuple<T1,T2> przykład — co oznacza nazwa, zapewnia typ wartości z elastycznością typów anonimowych. W tym artykule dowiesz się, kiedy należy wybrać jeden typ na drugim.
Użyteczność i funkcjonalność
Typy anonimowe zostały wprowadzone w języku C# 3.0 z wyrażeniami zapytań zintegrowanych z językiem (LINQ). Dzięki LINQ deweloperzy często projektują wyniki zapytań do typów anonimowych, które przechowują kilka wybranych właściwości z obiektów, z którymi pracują. Rozważmy poniższy przykład, który tworzy wystąpienie tablicy DateTime obiektów i iteruje przez nie projekcję w typ anonimowy z dwiema właściwościami.
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}");
}
Typy anonimowe są tworzone przy użyciu new
operatora, a nazwy i typy właściwości są wnioskowane z deklaracji. Jeśli co najmniej dwa anonimowe inicjatory obiektów w tym samym zestawie określ sekwencję właściwości, które znajdują się w tej samej kolejności i które mają te same nazwy i typy, kompilator traktuje obiekty jako wystąpienia tego samego typu. Współużytkują te same informacje o typie generowanym przez kompilator.
Poprzedni fragment kodu C# projektuje typ anonimowy z dwiema właściwościami, podobnie jak następująca klasa C# wygenerowana przez kompilator:
internal sealed class f__AnonymousType0
{
public string Formatted { get; }
public long Ticks { get; }
public f__AnonymousType0(string formatted, long ticks)
{
Formatted = formatted;
Ticks = ticks;
}
}
Aby uzyskać więcej informacji, zobacz typy anonimowe. Ta sama funkcja istnieje z krotkami podczas projekcji w zapytaniach LINQ, można wybrać właściwości w krotkach. Te krotki przepływają przez zapytanie, tak samo jak w przypadku typów anonimowych. Teraz rozważmy poniższy przykład przy użyciu elementu 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}");
}
Za pomocą System.Tuple<T1,T2>klasy wystąpienie uwidacznia właściwości elementu numerowanego, takie jak Item1
, i Item2
. Te nazwy właściwości mogą utrudnić zrozumienie intencji wartości właściwości, ponieważ nazwa właściwości udostępnia tylko porządkowy. Ponadto System.Tuple
typy są typami referencyjnymi class
. Jednak System.ValueTuple<T1,T2> jest to typ wartości struct
. Poniższy fragment kodu w języku C# używa metody ValueTuple<string, long>
do tworzenia projektu. W ten sposób przypisuje przy użyciu składni literału.
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}");
}
Aby uzyskać więcej informacji na temat krotki, zobacz Typy krotki (odwołanie w C#) lub Krotki (Visual Basic).
Poprzednie przykłady są jednak funkcjonalnie równoważne, jednak istnieją niewielkie różnice w ich użyteczności i ich podstawowych implementacjach.
Kompromisy
Warto zawsze używać ValueTupleTupletypów anonimowych i , ale należy wziąć pod uwagę pewne kompromisy. Typy ValueTuple są modyfikowalne, natomiast Tuple są tylko do odczytu. Typy anonimowe mogą być używane w drzewach wyrażeń, podczas gdy krotki nie mogą. Poniższa tabela zawiera omówienie niektórych kluczowych różnic.
Podstawowe różnice
Nazwisko | Modyfikator dostępu | Typ | Niestandardowa nazwa elementu członkowskiego | Obsługa dekonstrukcji | Obsługa drzewa wyrażeń |
---|---|---|---|---|---|
Typy anonimowe | internal |
class |
✔️ | ❌ | ✔️ |
Tuple | public |
class |
❌ | ❌ | ✔️ |
ValueTuple | public |
struct |
✔️ | ✔️ | ❌ |
Serializacja
Jedną z ważnych kwestii podczas wybierania typu jest to, czy trzeba będzie serializować. Serializacja jest proces konwersji stan obiektu do formularza, które mogą być utrwalone lub transportowane. Aby uzyskać więcej informacji, zobacz serializacji. Jeśli serializacja jest ważna, tworzenie elementu class
lub struct
jest preferowane w przypadku typów anonimowych lub krotki.
Wydajność
Wydajność między tymi typami zależy od scenariusza. Głównym wpływem jest kompromis między alokacjami a kopiowaniem. W większości scenariuszy wpływ jest niewielki. W przypadku wystąpienia poważnych skutków należy podjąć pomiary w celu poinformowania o decyzji.
Podsumowanie
Jako deweloper wybierający między krotkami a typami anonimowymi należy wziąć pod uwagę kilka czynników. Ogólnie rzecz biorąc, jeśli nie pracujesz z drzewami wyrażeń i dobrze znasz składnię krotki, wybierz ValueTuple typ wartości z elastycznością właściwości nazw. Jeśli pracujesz z drzewami wyrażeń i chcesz nazwać właściwości, wybierz typy anonimowe. W przeciwnym razie użyj polecenia Tuple.