Skip to content

[Proposal] Extend with expression to anonymous type #3530

Open
@leandromoh

Description

@leandromoh

Extend with expression to anonymous type

  • Proposed
  • Prototype: Done.
  • Implementation: Done,
  • Specification: Not Started

Speclet: https://github.com/dotnet/csharplang/blob/main/proposals/csharp-10.0/record-structs.md

Summary

The with expression, introduced in C# 9, is designed to produce a copy of the receiver expression, in a "non-destructive mutation" manner.

This proposal extend with expression to anonymous type, since they are also immutable, the feature may fit well with them too.

Note: F# has a very similar feature called copy and update record expressions.

Motivation

Reduce boilerplate code to create new instances of anonymous type based on already existing instance.

Current approach:

var person = new { FirstName = "Scott", LastName = "Hunter", Age = 25 };

var otherPerson = new { person.FirstName, LastName = "Hanselman", person.Age };

Proposed:

var person = new { FirstName = "Scott", LastName = "Hunter", Age = 25 };

var otherPerson = person with { LastName = "Hanselman" };

Detailed design

The syntax is the same described in with expression section of the record proposal, that is

with_expression
    : switch_expression
    | switch_expression 'with' '{' member_initializer_list? '}'
    ;

member_initializer_list
    : member_initializer (',' member_initializer)*
    ;

member_initializer
    : identifier '=' expression
    ;

In the context of this proposal, the receiver expression must be an anonymous object. Also, different of the original with proposal, the anonymous type will not need contains an accessible "clone" method, since the copy can be done by the compiler just calling the constructor of the anonymous type, what maintain how anonymous types are emitted.

Currently, each anonymous type's property has a correspondent parameter in the type constructor, with same name and type. So compiler must pass the correspondent member expression for each argument. Case the member exists in the member_initializer_list, compiler must pass as equivalent argument the expression on the right side of the member_initializer.

The orders each member_initializer appears is irrelevant, since they will be processed in constructor call, therefore in the order of the parameters.

Drawbacks

None.

Alternatives

One workaround is use reflection and expression trees to build such anonymous type's constructor call. However theses features have performance cost and are, perhaps, not common for all programmers of the language. It also requires new intermediary anonymous instances to represent the properties and values that we will be changed. Here is a gist with my workaround with follow usage:

var otherPerson = person.With(new { LastName = "Hanselman" });

Unresolved questions

Design meetings

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions