Skip to content

Commit 550b183

Browse files
authored
Add with-expressions to anonymous types (#27111)
* Add with-expressions to anonymous types Fixes #26770 With-expressions can be used with anonymous types. * code review
1 parent 93caa7f commit 550b183

File tree

8 files changed

+32
-11
lines changed

8 files changed

+32
-11
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ Anonymous types are [`class`](../../language-reference/keywords/class.md) types
4444

4545
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.
4646

47+
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:
48+
49+
:::code language="csharp" source="snippets/anonymous-types/Program.cs" ID="snippet02":::
50+
4751
You cannot declare a field, a property, an event, or the return type of a method as having an anonymous type. Similarly, you cannot declare a formal parameter of a method, property, constructor, or indexer as having an anonymous type. To pass an anonymous type, or a collection that contains anonymous types, as an argument to a method, you can declare the parameter as type `object`. However, using `object` for anonymous types defeats the purpose of strong typing. If you must store query results or pass them outside the method boundary, consider using an ordinary named struct or class instead of an anonymous type.
4852

4953
Because the <xref:System.Object.Equals%2A> and <xref:System.Object.GetHashCode%2A> methods on anonymous types are defined in terms of the `Equals` and `GetHashCode` methods of the properties, two instances of the same anonymous type are equal only if all their properties are equal.

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace anonymous_types
66
{
77
class Product
88
{
9-
public string Color {get;set;}
9+
public string? Color {get;set;}
1010
public decimal Price {get;set;}
1111
}
1212
class Anonymous
@@ -16,7 +16,7 @@ static void Main()
1616
// don't show this unless you add a bunch more
1717
// properties to the type. otherwise it obviates the
1818
// need for the anonymous type
19-
List<Product> products = new List<Product>()
19+
List<Product> products = new ()
2020
{
2121
new Product() { Color="Orange", Price=2.00M},
2222
};
@@ -31,6 +31,13 @@ from prod in products
3131
Console.WriteLine("Color={0}, Price={1}", v.Color, v.Price);
3232
}
3333
//</snippet81>
34+
35+
// <Snippet02>
36+
var apple = new { Item = "apples", Price = 1.35 };
37+
var onSale = apple with { Price = 0.79 };
38+
Console.WriteLine(apple);
39+
Console.WriteLine(onSale);
40+
// </Snippet02>
3441
}
3542
}
3643
}

docs/csharp/fundamentals/types/snippets/anonymous-types/anonymous-types.csproj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFramework>net5.0</TargetFramework>
5+
<TargetFramework>net6.0</TargetFramework>
66
<RootNamespace>anonymous_types</RootNamespace>
7+
<Nullable>enable</Nullable>
8+
<ImplicitUsings>enable</ImplicitUsings>
79
</PropertyGroup>
810

911
</Project>

docs/csharp/language-reference/operators/snippets/with-expression/BasicExample.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22

33
public class WithExpressionBasicExample
44
{
@@ -20,5 +20,11 @@ public static void Main()
2020
Console.WriteLine($"{nameof(p3)}: {p3}"); // output: p3: NamedPoint { Name = C, X = 0, Y = 4 }
2121

2222
Console.WriteLine($"{nameof(p1)}: {p1}"); // output: p1: NamedPoint { Name = A, X = 0, Y = 0 }
23+
24+
var apples = new { Item = "Apples", Price = "1.19" };
25+
Console.WriteLine($"original apples: {apples}");
26+
var saleApples = apples with { Price = "0.79" };
27+
Console.WriteLine($"sale apples: {saleApples}");
28+
2329
}
24-
}
30+
}

docs/csharp/language-reference/operators/snippets/with-expression/ExampleWithReferenceType.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using System.Collections.Generic;
33

44
public class ExampleWithReferenceType
@@ -19,5 +19,6 @@ public static void Main()
1919
original.Tags.Add("C");
2020
Console.WriteLine($"Tags of {nameof(copy)}: {copy.PrintTags()}");
2121
// output: Tags of copy: A, B, C
22+
2223
}
23-
}
24+
}

docs/csharp/language-reference/operators/snippets/with-expression/with-expression.csproj

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFramework>net5.0</TargetFramework>
6-
<LangVersion>9.0</LangVersion>
5+
<TargetFramework>net6.0</TargetFramework>
76
<RootNamespace>with_expression</RootNamespace>
87
<StartupObject>with_expression.Program</StartupObject>
8+
<Nullable>enable</Nullable>
9+
<ImplicitUsings>enable</ImplicitUsings>
910
</PropertyGroup>
1011

1112
</Project>

docs/csharp/language-reference/operators/with-expression.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Available in C# 9.0 and later, a `with` expression produces a copy of its operan
1616

1717
As the preceding example shows, you use [object initializer](../../programming-guide/classes-and-structs/object-and-collection-initializers.md) syntax to specify what members to modify and their new values.
1818

19-
In C# 9.0, a left-hand operand of a `with` expression must be of a [record type](../builtin-types/record.md). Beginning with C# 10, a left-hand operand of a `with` expression can also be of a [structure type](../builtin-types/struct.md).
19+
In C# 9.0, a left-hand operand of a `with` expression must be of a [record type](../builtin-types/record.md). Beginning with C# 10, a left-hand operand of a `with` expression can also be of a [structure type](../builtin-types/struct.md) or an [anonymous type](../../fundamentals/types/anonymous-types.md).
2020

2121
The result of a `with` expression has the same run-time type as the expression's operand, as the following example shows:
2222

docs/csharp/whats-new/csharp-10.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ You can declare value type records using the [`record struct` or `readonly recor
3737
C# 10 introduces the following improvements related to structure types:
3838

3939
- You can declare an instance parameterless constructor in a structure type and initialize an instance field or property at its declaration. For more information, see the [Parameterless constructors and field initializers](../language-reference/builtin-types/struct.md#parameterless-constructors-and-field-initializers) section of the [Structure types](../language-reference/builtin-types/struct.md) article.
40-
- A left-hand operand of the [`with` expression](../language-reference/operators/with-expression.md) can be of any structure type.
40+
- A left-hand operand of the [`with` expression](../language-reference/operators/with-expression.md) can be of any structure type or an anonymous (reference) type.
4141

4242
## Interpolated string handler
4343

0 commit comments

Comments
 (0)