Skip to content

Commit fd0cceb

Browse files
committed
add more examples for tuples
1 parent 97f791d commit fd0cceb

File tree

2 files changed

+82
-19
lines changed

2 files changed

+82
-19
lines changed

docs/csharp/fundamentals/types/anonymous-types.md

Lines changed: 82 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: "Anonymous Types and Tuples"
33
description: Anonymous types in C# encapsulate a set of read-only properties in an object without having to explicitly define a type. The compiler generates a name.
4-
ms.date: 10/13/2025
4+
ms.date: 11/06/2025
55
f1_keywords:
66
- "anonymousObject_CSharpKeyword"
77
helpviewer_keywords:
@@ -10,13 +10,13 @@ helpviewer_keywords:
1010
---
1111
# Anonymous types and tuples
1212

13-
Anonymous types provide a convenient way to encapsulate a set of read-only properties into a single object without having to explicitly define a type first. The type name is generated by the compiler and isn't available at the source code level. The type of each property is inferred by the compiler.
13+
Anonymous types provide a convenient way to encapsulate a set of read-only properties into a single object without having to explicitly define a type first. The compiler generates the type name, and it's not available at the source code level. The compiler infers the type of each property.
1414

1515
In most scenarios, [tuples](../../language-reference/builtin-types/value-tuples.md) are the preferred choice over anonymous types. Tuples provide better performance, support deconstruction, and offer more flexible syntax. Use anonymous types primarily when you need expression tree support or when working with code that requires reference types.
1616

1717
## Anonymous types vs tuples
1818

19-
Both anonymous types and tuples let you group multiple values without defining a named type. However, tuples are the preferred choice in most scenarios because they provide better performance and more flexibility. The following table summarizes the key differences:
19+
Both anonymous types and tuples let you group multiple values without defining a named type. However, tuples have better language support and compile to a more efficient data structure. The following table summarizes the key differences:
2020

2121
| Feature | Anonymous types | Tuples |
2222
|---------|----------------|--------|
@@ -41,29 +41,96 @@ The following example shows how tuples provide similar functionality to anonymou
4141

4242
:::code language="csharp" source="snippets/anonymous-types/Program.cs" ID="TupleExample":::
4343

44+
### Tuple deconstruction
45+
46+
You can deconstruct a tuple into separate variables, which provides a convenient way to work with individual tuple elements. C# supports several ways to deconstruct tuples:
47+
48+
```csharp
49+
// Define a method that returns a tuple
50+
(string Name, int Age, string City) GetPersonInfo()
51+
{
52+
return ("Alice", 30, "Seattle");
53+
}
54+
55+
// Deconstruct using var for all variables
56+
var (name, age, city) = GetPersonInfo();
57+
Console.WriteLine($"{name} is {age} years old and lives in {city}");
58+
// Output: Alice is 30 years old and lives in Seattle
59+
60+
// Deconstruct with explicit types
61+
(string personName, int personAge, string personCity) = GetPersonInfo();
62+
Console.WriteLine($"{personName}, {personAge}, {personCity}");
63+
64+
// Deconstruct into existing variables
65+
string existingName;
66+
int existingAge;
67+
string existingCity;
68+
(existingName, existingAge, existingCity) = GetPersonInfo();
69+
70+
// Deconstruct and discard unwanted values using the discard pattern (_)
71+
var (name2, _, city2) = GetPersonInfo();
72+
Console.WriteLine($"{name2} lives in {city2}");
73+
// Output: Alice lives in Seattle
74+
```
75+
76+
Deconstruction is useful in loops and pattern matching scenarios:
77+
78+
```csharp
79+
var people = new List<(string Name, int Age)>
80+
{
81+
("Bob", 25),
82+
("Carol", 35),
83+
("Dave", 40)
84+
};
85+
86+
foreach (var (name, age) in people)
87+
{
88+
Console.WriteLine($"{name} is {age} years old");
89+
}
90+
```
91+
92+
### Tuples as a method return type
93+
94+
A common use case for tuples is as a method return type. Instead of defining `out` parameters, you can group method results in a tuple. The following example demonstrates using tuples with dictionary lookups to return configuration ranges:
95+
96+
```csharp
97+
var configLookup = new Dictionary<int, (int Min, int Max)>()
98+
{
99+
[2] = (4, 10),
100+
[4] = (10, 20),
101+
[6] = (0, 23)
102+
};
103+
104+
if (configLookup.TryGetValue(4, out (int Min, int Max) range))
105+
{
106+
Console.WriteLine($"Found range: min is {range.Min}, max is {range.Max}");
107+
}
108+
// Output: Found range: min is 10, max is 20
109+
```
110+
111+
This pattern is useful when working with methods that need to return both a success indicator and multiple result values. The tuple allows you to use named fields (`Min` and `Max`) instead of generic names like `Item1` and `Item2`, making the code more readable and self-documenting.
112+
44113
## When to use anonymous types
45114

46115
Use anonymous types when:
47116

48-
- You're working with expression trees (for example, in some LINQ providers).
117+
- You're working with expression trees (for example, in some Microsoft Language-Integrated Query (LINQ) providers).
49118
- You need the object to be a reference type.
50119
- You're projecting query results in LINQ and want named properties without defining a class.
51120

52-
For more information about choosing between anonymous types and tuples, see [Choosing between anonymous and tuple types](../../../standard/base-types/choosing-between-anonymous-and-tuple.md).
53-
54121
The most common scenario is to initialize an anonymous type with properties from another type. In the following example, assume that a class exists that is named `Product`. Class `Product` includes `Color` and `Price` properties, together with other properties that you aren't interested in:
55122

56123
:::code language="csharp" source="snippets/anonymous-types/Program.cs" ID="ProductDefinition":::
57124

58125
The anonymous type declaration starts with the [`new`](../../language-reference/operators/new-operator.md) operator together with an [object initializer](../../programming-guide/classes-and-structs/object-and-collection-initializers.md). The declaration initializes a new type that uses only two properties from `Product`. Anonymous types are typically used in the [`select`](../../language-reference/keywords/select-clause.md) clause of a query expression to return a smaller amount of data. For more information about queries, see [LINQ in C#](../../linq/index.md).
59126

60-
If you don't specify member names in the anonymous type, the compiler gives the anonymous type members the same name as the property being used to initialize them. You provide a name for a property that's being initialized with an expression, as shown in the previous example.
127+
If you don't specify member names in the anonymous type, the compiler gives the anonymous type members the same name as the property used to initialize them. You provide a name for a property that's being initialized with an expression, as shown in the previous example.
61128

62-
In the following example, the names of the properties of the anonymous type are `Color` and `Price`. The instances are item from the `products` collection of `Product` types:
129+
In the following example, the names of the properties of the anonymous type are `Color` and `Price`. The instances are items from the `products` collection of `Product` types:
63130

64131
:::code language="csharp" source="snippets/anonymous-types/Program.cs" ID="snippet81":::
65132

66-
## Projection initializers with anonymous types
133+
### Projection initializers with anonymous types
67134

68135
Anonymous types support *projection initializers*, which allow you to use local variables or parameters directly without explicitly specifying the member name. The compiler infers the member names from the variable names. The following example demonstrates this simplified syntax:
69136

@@ -75,31 +142,31 @@ This simplified syntax is useful when creating anonymous types with many propert
75142

76143
The member name isn't inferred in the following cases:
77144

78-
- The candidate name is a duplicate of another property member in the same anonymous type, either explicit or implicit.
145+
- The candidate name duplicates another property member in the same anonymous type, either explicit or implicit.
79146
- The candidate name isn't a valid identifier (for example, it contains spaces or special characters).
80147

81148
In these cases, you must explicitly specify the member name.
82149

83150
> [!TIP]
84151
> You can use .NET style rule [IDE0037](../../../fundamentals/code-analysis/style-rules/ide0037.md) to enforce whether inferred or explicit member names are preferred.
85152
86-
It's also possible to define a field by object of another type: class, struct, or even another anonymous type. It's done by using the variable holding this object just like in the following example, where two anonymous types are created using already instantiated user-defined types. In both cases, the `product` field in the anonymous type `shipment` and `shipmentWithBonus` is of type `Product` containing its default values of each field. And the `bonus` field is of anonymous type created by the compiler.
153+
You can also define a field by using an object of another type: class, struct, or even another anonymous type. To do this, use the variable that holds this object. The following example shows two anonymous types that use already instantiated user-defined types. In both cases, the `product` field in the anonymous types `shipment` and `shipmentWithBonus` is of type `Product` and contains the default values of each field. The `bonus` field is of an anonymous type created by the compiler.
87154

88155
:::code language="csharp" source="snippets/anonymous-types/Program.cs" ID="snippet03":::
89156

90-
Typically, when you use an anonymous type to initialize a variable, you declare the variable as an implicitly typed local variable by using [var](../../language-reference/statements/declarations.md#implicitly-typed-local-variables). The type name can't be specified in the variable declaration because only the compiler has access to the underlying name of the anonymous type. For more information about `var`, see [Implicitly Typed Local Variables](../../programming-guide/classes-and-structs/implicitly-typed-local-variables.md).
157+
Typically, when you use an anonymous type to initialize a variable, you declare the variable as an implicitly typed local variable by using [var](../../language-reference/statements/declarations.md#implicitly-typed-local-variables). You can't specify the type name in the variable declaration because only the compiler has access to the underlying name of the anonymous type. For more information about `var`, see [Implicitly Typed Local Variables](../../programming-guide/classes-and-structs/implicitly-typed-local-variables.md).
91158

92159
You can create an array of anonymously typed elements by combining an implicitly typed local variable and an implicitly typed array, as shown in the following example.
93160

94161
```csharp
95162
var anonArray = new[] { new { name = "apple", diam = 4 }, new { name = "grape", diam = 1 }};
96163
```
97164

98-
Anonymous types are [`class`](../../language-reference/keywords/class.md) types that derive directly from [`object`](../../language-reference/builtin-types/reference-types.md), and that can't be cast to any type except [`object`](../../language-reference/builtin-types/reference-types.md). The compiler provides a name for each anonymous type, although your application can't access it. From the perspective of the common language runtime, an anonymous type is no different from any other reference type.
165+
Anonymous types are [`class`](../../language-reference/keywords/class.md) types that derive directly from [`object`](../../language-reference/builtin-types/reference-types.md), and you can't cast them to any type except [`object`](../../language-reference/builtin-types/reference-types.md). The compiler provides a name for each anonymous type, although your application can't access it. From the perspective of the common language runtime, an anonymous type is no different from any other reference type.
99166

100167
If two or more anonymous object initializers in an assembly specify a sequence of properties that are in the same order and that have the same names and types, the compiler treats the objects as instances of the same type. They share the same compiler-generated type information.
101168

102-
Anonymous types support non-destructive mutation in the form of [with expressions](../../language-reference/operators/with-expression.md). This enables you to create a new instance of an anonymous type where one or more properties have new values:
169+
Anonymous types support non-destructive mutation in the form of [with expressions](../../language-reference/operators/with-expression.md). This feature enables you to create a new instance of an anonymous type where one or more properties have new values:
103170

104171
:::code language="csharp" source="snippets/anonymous-types/Program.cs" ID="snippet02":::
105172

@@ -109,7 +176,7 @@ Because the <xref:System.Object.Equals%2A> and <xref:System.Object.GetHashCode%2
109176

110177
> [!NOTE]
111178
> The [accessibility level](../../programming-guide/classes-and-structs/access-modifiers.md) of an anonymous type is `internal`. Hence, two anonymous types defined in different assemblies aren't of the same type.
112-
> Therefore instances of anonymous types can't be equal to each other when defined in different assemblies, even when having all their properties equal.
179+
> Therefore, instances of anonymous types can't be equal to each other when defined in different assemblies, even when having all their properties equal.
113180
114181
Anonymous types do override the <xref:System.Object.ToString%2A> method, concatenating the name and `ToString` output of every property surrounded by curly braces.
115182

docs/csharp/fundamentals/types/snippets/anonymous-types/Program.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,6 @@ from prod in products
8787
// Equivalent using a tuple with named elements.
8888
var tupleProduct = (Name: "Widget", Price: 19.99M);
8989
Console.WriteLine($"Tuple: {tupleProduct.Name} costs ${tupleProduct.Price}");
90-
91-
// Tuples support deconstruction.
92-
var (productName, productPrice) = tupleProduct;
93-
Console.WriteLine($"Deconstructed: {productName} costs ${productPrice}");
9490
// </TupleExample>
9591
}
9692
}

0 commit comments

Comments
 (0)