Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Krotki zapewniają lekką strukturę danych dla wielu elementów w jednej strukturze. Są one preferowanym wyborem w przypadku typów anonimowych. Krotki zapewniają lepszą wydajność, obsługę dekonstrukcji i oferują bardziej elastyczną składnię.
Typy anonimowe zapewniają wygodny sposób hermetyzacji zestawu właściwości tylko do odczytu w jednym obiekcie bez konieczności jawnego definiowania typu. Kompilator generuje nazwę typu i nie jest dostępny na poziomie kodu źródłowego. Kompilator wywnioskuje typ każdej właściwości. Używaj typów anonimowych głównie wtedy, gdy potrzebujesz obsługi drzewa wyrażeń lub podczas pracy z kodem, który wymaga typów odwołań.
Krotki vs typy anonimowe
Zarówno krotki, jak i typy anonimowe umożliwiają grupowanie wielu wartości bez definiowania typu z nazwą. Jednak krotki mają lepsze wsparcie językowe i kompilują się do bardziej wydajnej struktury danych. Poniższa tabela zawiera podsumowanie kluczowych różnic:
| Funkcja | Typy anonimowe | Krotki |
|---|---|---|
| Typ | Typ odwołania (class) |
Typ wartości (struct) |
| Performance | Alokacja sterty | Alokacja stosu (lepsza wydajność) |
| Mutability | Właściwości tylko do odczytu | Pola modyfikowalne |
| Dekonstrukcja | Niewspierane | Wsparte |
| Drzewa wyrażeń | Wsparte | Niewspierane |
| Modyfikator dostępu | internal |
public |
| Nazwy członków | Wymagane lub wnioskowane | Opcjonalne (z nazwami domyślnymi, takimi jak Item1, Item2) |
Kiedy należy używać krotek
Użyj krotki, gdy:
- Aby uzyskać lepszą wydajność, zastosuj alokację stosu.
- Chcesz zdekonstruować wartości na oddzielne zmienne.
- Zwracasz wiele wartości z metody.
- Nie potrzebujesz obsługi drzewa wyrażeń.
W poniższym przykładzie pokazano, jak krotki zapewniają podobne funkcje do typów anonimowych ze składnią czystszą:
// Tuple with named elements.
var tupleProduct = (Name: "Widget", Price: 19.99M);
Console.WriteLine($"Tuple: {tupleProduct.Name} costs ${tupleProduct.Price}");
// Equivalent example using anonymous types.
var anonymousProduct = new { Name = "Widget", Price = 19.99M };
Console.WriteLine($"Anonymous: {anonymousProduct.Name} costs ${anonymousProduct.Price}");
Dekonstrukcja krotki
Możesz zdekonstruować krotkę na osobne zmienne, co zapewnia wygodny sposób pracy z poszczególnymi elementami krotki. Język C# obsługuje kilka sposobów dekonstrukcji krotek.
static (string Name, int Age, string City) GetPersonInfo()
{
return ("Alice", 30, "Seattle");
}
// Deconstruct using var for all variables
var (name, age, city) = GetPersonInfo();
Console.WriteLine($"{name} is {age} years old and lives in {city}");
// Output: Alice is 30 years old and lives in Seattle
// Deconstruct with explicit types
(string personName, int personAge, string personCity) = GetPersonInfo();
Console.WriteLine($"{personName}, {personAge}, {personCity}");
// Deconstruct into existing variables
string existingName;
int existingAge;
string existingCity;
(existingName, existingAge, existingCity) = GetPersonInfo();
// Deconstruct and discard unwanted values using the discard pattern (_)
var (name2, _, city2) = GetPersonInfo();
Console.WriteLine($"{name2} lives in {city2}");
// Output: Alice lives in Seattle
Dekonstrukcja jest przydatna w pętlach i scenariuszach dopasowywania wzorców:
var people = new List<(string Name, int Age)>
{
("Bob", 25),
("Carol", 35),
("Dave", 40)
};
foreach (var (personName2, personAge2) in people)
{
Console.WriteLine($"{personName2} is {personAge2} years old");
}
Krotki jako typ zwracany przez metodę
Typowy przypadek użycia krotki to jej zastosowanie jako typu zwracanego przez metodę. Zamiast definiować out parametry, można grupować wyniki metody w krotkę. Nie można zwrócić typu anonimowego z metody, ponieważ nie ma nazwy, a zwracany typ nie może być zadeklarowany.
W poniższym przykładzie pokazano użycie krotki z odnośnikami słownika w celu zwrócenia zakresów konfiguracji:
var configLookup = new Dictionary<int, (int Min, int Max)>()
{
[2] = (4, 10),
[4] = (10, 20),
[6] = (0, 23)
};
if (configLookup.TryGetValue(4, out (int Min, int Max) range))
{
Console.WriteLine($"Found range: min is {range.Min}, max is {range.Max}");
}
// Output: Found range: min is 10, max is 20
Ten wzorzec jest przydatny podczas pracy z metodami, które muszą zwracać zarówno wskaźnik sukcesu, jak i wiele wartości wyników. Krotka umożliwia używanie nazwanych pól (Min i Max) zamiast ogólnych nazw, takich jak Item1 i Item2, dzięki czemu kod jest bardziej czytelny i samodokumentujący.
Kiedy należy używać typów anonimowych
Użyj typów anonimowych, gdy:
- Pracujesz z drzewami wyrażeń (na przykład w niektórych dostawcach zapytań MICROSOFT Language-Integrated Query (LINQ).
- Musisz mieć obiekt jako typ odwołania.
Najczęstszym scenariuszem jest zainicjowanie typu anonimowego z właściwościami z innego typu. W poniższym przykładzie przyjęto założenie, że istnieje klasa o nazwie Product. Klasa Product zawiera właściwości Color i Price wraz z innymi właściwościami, które Cię nie interesują.
class Product
{
public string? Color { get; init; }
public decimal Price { get; init; }
public string? Name { get; init; }
public string? Category { get; init; }
public string? Size { get; init; }
}
Deklaracja typu anonimowego rozpoczyna się poprzez operator new razem z inicjatorem obiektu. Deklaracja inicjuje nowy typ, który używa tylko dwóch właściwości z klasy Product. Typy anonimowe są zwykle używane w select klauzuli wyrażenia zapytania w celu zwrócenia mniejszej ilości danych. Aby uzyskać więcej informacji na temat zapytań, zobacz LINQ w języku C#.
Jeśli nie określisz nazw składowych w typie anonimowym, kompilator nadaje anonimowym składowym typu taką samą nazwę jak właściwość używana do ich inicjowania. Należy podać nazwę właściwości, która jest inicjowana za pomocą wyrażenia, jak pokazano w poprzednim przykładzie.
W poniższym przykładzie nazwy właściwości typu anonimowego to Color i Price. Wystąpienia to elementy z products kolekcji Product typów:
var productQuery =
from prod in products
select new { prod.Color, prod.Price };
foreach (var v in productQuery)
{
Console.WriteLine("Color={0}, Price={1}", v.Color, v.Price);
}
Inicjalizatory projekcji z typami anonimowymi
Typy anonimowe obsługują inicjatory projekcji, które umożliwiają bezpośrednie używanie zmiennych lokalnych lub parametrów bez jawnego określenia nazwy elementu członkowskiego. Kompilator wywnioskuje nazwy składowych z nazw zmiennych. W poniższym przykładzie pokazano tę uproszczoną składnię:
// Explicit member names.
var personExplicit = new { FirstName = "Kyle", LastName = "Mit" };
// Projection initializers (inferred member names).
var firstName = "Kyle";
var lastName = "Mit";
var personInferred = new { firstName, lastName };
// Both create equivalent anonymous types with the same property names.
Console.WriteLine($"Explicit: {personExplicit.FirstName} {personExplicit.LastName}");
Console.WriteLine($"Inferred: {personInferred.firstName} {personInferred.lastName}");
Ta uproszczona składnia jest przydatna podczas tworzenia typów anonimowych z wieloma właściwościami:
var title = "Software Engineer";
var department = "Engineering";
var salary = 75000;
// Using projection initializers.
var employee = new { title, department, salary };
// Equivalent to explicit syntax:
// var employee = new { title = title, department = department, salary = salary };
Console.WriteLine($"Title: {employee.title}, Department: {employee.department}, Salary: {employee.salary}");
Nazwa elementu członkowskiego nie jest wywnioskowana w następujących przypadkach:
- Nazwa kandydata duplikuje inną część składową właściwości w tym samym typie anonimowym, jawnym lub niejawnym.
- Nazwa kandydata nie jest prawidłowym identyfikatorem (na przykład zawiera spacje lub znaki specjalne).
W takich przypadkach należy jawnie określić nazwę elementu członkowskiego.
Napiwek
Możesz użyć reguły stylu platformy .NET IDE0037, aby wymusić, czy wywnioskowane lub jawne nazwy elementów członkowskich są preferowane.
Można również zdefiniować pole przy użyciu obiektu innego typu: klasy, struktury lub nawet innego typu anonimowego. W tym celu użyj zmiennej, która przechowuje ten obiekt. W poniższym przykładzie przedstawiono dwa typy anonimowe, które używają już zainicjowanych typów zdefiniowanych przez użytkownika. W obu przypadkach pole product w anonimowych typach shipment i shipmentWithBonus jest typu Product i zawiera wartości domyślne każdego pola. Pole bonus jest typu anonimowego utworzonego przez kompilator.
var product = new Product();
var bonus = new { note = "You won!" };
var shipment = new { address = "Nowhere St.", product };
var shipmentWithBonus = new { address = "Somewhere St.", product, bonus };
Zazwyczaj w przypadku użycia typu anonimowego do zainicjowania zmiennej należy zadeklarować zmienną jako niejawnie typizowanej zmiennej lokalnej przy użyciu zmiennej var. Nie można określić nazwy typu w deklaracji zmiennej, ponieważ tylko kompilator ma dostęp do bazowej nazwy typu anonimowego. Aby uzyskać więcej informacji na temat varprogramu , zobacz Niejawnie typizowane zmienne lokalne.
Można utworzyć tablicę anonimowo wpisanych elementów, łącząc niejawnie typizowanej zmiennej lokalnej i niejawnie typizowanej tablicy, jak pokazano w poniższym przykładzie.
var anonArray = new[] { new { name = "apple", diam = 4 }, new { name = "grape", diam = 1 }};
Typy anonimowe to class typy, które pochodzą bezpośrednio z object, i nie można ich rzutować do żadnego typu z wyjątkiem object. Kompilator udostępnia nazwę dla każdego typu anonimowego, chociaż aplikacja nie może uzyskać do niej dostępu. Z perspektywy środowiska uruchomieniowego języka wspólnego typ anonimowy nie różni się od innego typu odwołania.
Jeśli co najmniej dwa anonimowe inicjatory obiektów w zestawie określ sekwencję właściwości, które znajdują się w tej samej kolejności i które mają takie same nazwy i typy, kompilator traktuje obiekty jako wystąpienia tego samego typu. Współużytkują te same informacje o typie generowanym przez kompilator.
Typy anonimowe obsługują mutację niedestrukcyjną w postaci wyrażeń. Ta funkcja umożliwia utworzenie nowego wystąpienia typu anonimowego, w którym co najmniej jedna właściwości ma nowe wartości:
var apple = new { Item = "apples", Price = 1.35 };
var onSale = apple with { Price = 0.79 };
Console.WriteLine(apple);
Console.WriteLine(onSale);
Nie można zadeklarować pola, właściwości, zdarzenia lub zwracanego typu metody jako typu anonimowego. Podobnie nie można zadeklarować parametru formalnego metody, właściwości, konstruktora lub indeksatora jako typu anonimowego. Aby przekazać typ anonimowy lub kolekcję zawierającą typy anonimowe, jako argument metody, można zadeklarować parametr jako typ object. Jednak użycie object w przypadku typów anonimowych pokonuje cel silnego pisania. Jeśli musisz przechowywać wyniki zapytania lub przekazywać je poza granicę metody, rozważ użycie zwykłej, nazwanej struktury lub klasy zamiast typu anonimowego.
Equals Ponieważ metody i GetHashCode dla typów anonimowych są zdefiniowane pod względem Equals właściwości iGetHashCode, dwa wystąpienia tego samego typu anonimowego są równe tylko wtedy, gdy wszystkie ich właściwości są równe.
Uwaga
Poziom dostępu typu anonimowego to . W związku z tym dwa anonimowe typy zdefiniowane w różnych zestawach nie są tego samego typu. W związku z tym wystąpienia typów anonimowych nie mogą być równe sobie podczas definiowania w różnych zestawach, nawet jeśli mają wszystkie ich właściwości równe.
Typy anonimowe zastępują metodę ToString , łącząc nazwę i ToString dane wyjściowe każdej właściwości otoczonej nawiasami klamrowymi.
var v = new { Title = "Hello", Age = 24 };
Console.WriteLine(v.ToString()); // "{ Title = Hello, Age = 24 }"
Zobacz także
- Typy tupli
- Wybieranie między typami anonimowymi i krotek
- Inicjatory obiektów i kolekcji
- LINQ w C#