-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Proposal] Extend with expression to anonymous type #3530
Comments
In case of someone is looking for such feature, here is a gist with my workaround. with follow usage: var otherPerson = person.With(new { LastName = "Hanselman" }); |
I'm willing to champion this extension, with the caveat that it won't come in 9 :) |
Makes sense, you could probably consider anonymous types to automatically be records given they already have value equality, no? |
Possibly. It would still depend on whether we add anything else to the definition of a record type. I'm not sure whether we'd add a copy constructor, for example, or if we'd just do the copy at call site. |
@leandromoh this will need an actual spec. Would you be willing to take a stab at writing one? It would need to be an extension to the existing records proposal. |
Would "withers" come for free if an anonymous type definition was modified so that:
|
Probably, but then every piece of code generates an anonymous type would need to be recompiled to work with this feature. |
@333fred Since anonymous types can't be exposed publicly, why would that be a problem? |
I suppose it might be fine. Still, we need a spec to actually evaluate this :) |
@333fred I would like to do it, of course. I just need to know exactly what and how to do it. |
Basically, look at the existing records proposal (https://github.com/dotnet/csharplang/blob/master/proposals/records.md#with-expression) and detail exactly how it would need to be modified in order to support |
Given that the compiler has complete control over the definition and use of anonymous types it might make more sense to have the compiler treat it special and just emit its own call to the constructor manually copying the source properties, as suggested in the original post. I don't see any other added benefit to adding/changing the members of the anonymous type, it's not like external code can take advantage of it outside of reflection. |
Will it allow adding properties, since anonymous types do not have declared type like records? |
That sounds interesting, although I'd a little concerned that it would make it very easily to make a mistake: var person1 = new { Name = "Bill", Age = 45 };
var person2 = person1 with { AGe = 46 }; // oops! |
Right. That seems like VB's implicit variable declaration, which is known to be error-prone... |
@HaloFour perhaps 'anon with' {x=y} requires x to be an already existing variable (ie, you can't extend an anon type with 'with') |
@leandromoh it's a good start, but it needs to be more concrete. A spec isn't a guess about how to implement something: it lays out exactly how it will be implemented. |
@333fred okay, I will try detail it more, but probably it will need some adjusts of you after all. |
@ChayimFriedman2 @HaloFour javascript has a spread operator that makes exactly this. example: var obj1 = { name: "bob", type:"Apple" };
var obj2 = { name: "joe", price: 0.20 };
var merge = {...obj1, ...obj2, age: 34 };
console.log(merge);
/* {
age: 34
name: "joe"
price: 0.2
type: "Apple"
}; */ If objects have common properties the priority will be right to left. The object the most at the right will have priority over the one at its left and so on. If we could expand C#'s anonymous type in a similar way would be amazing. |
If this were pre-C#-7.0 I would think this was a great idea. Post-C#-7.0 I don't see any point of putting development work into anonymous types. Tuples are better in nearly every respect - they are structs, they can be return types and parameter types, and if you really really need to mutate them, you already can. Given that, how often, and why, do you really need to "mutate" an anonymous type? The use cases for this seem vanishingly small. |
@MgSam this proposal enables anonymous types practically act like anonymous record types, as they have the rest of the properties of a record already: value equality, ToString, etc. F# already have anonymous record types. |
It would be also cool if those var files = Directory.EnumerateFiles(dir, "*.*", SearchOption.TopDirectoryOnly)
.Select(x => new FileInfo(x))
.Select(x => new
{
File = x,
FileName = x.Name,
Match = myRegex.Match(x.Name)
})
.Where(x => x.Match.Success)
.Select(x => x with
{
Group = x.Match.Groups[1].Value,
FileNumber = x.Match.Groups[2].Value
})); The Resulting anonymous type should then contain
|
any progress on this? my use case is flattening groupby data var group = data.GroupBy(a=>a.KeyProp).Select(a=>new {KeyProp = a.Key, Items = new {...}});
var flat = group.SelectMany(a=>a.Items.Select(b=>b with {Key = a.KeyProp})); |
but using with expression for extending objects can be misleading.
var flat = group.SelectMany(a=>a.Items.Select(b=>new {Key = a.KeyProp, ...b})); |
Introducing spread operator would be much bigger feature. And there would be complicated cases where Here using |
On the other hand, the analogous F# feature does allow exactly this on anonymous record types. |
Such errors can be avoided by using intellisense, which should know existing anonymous properties. |
It feels like extension with members that differ only by case should be a warning that needs to be suppressed to do deliberately ... Is "this should be a warning" a language or implementation discussion? |
Extend with expression to anonymous type
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:
Proposed:
Detailed design
The syntax is the same described in with expression section of the record proposal, that is
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 themember_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:
Unresolved questions
Design meetings
The text was updated successfully, but these errors were encountered: