[Proposal]: Collection Expression Arguments #8886
Replies: 13 comments 129 replies
-
How about List<string> s = [a, b, c] where { capacity: 32 };
HashSet<Foo> s = [a, b, c] where { comparer: comp }; ? Asking GPT-4o which one is preferred, and this is the response (click "Details" to expand):
Both designs have merits, but I lean toward the second design for its readability and clarity. Here’s a comparison:
1. First Design:
Pros:
Cons:
2. Second Design:
Pros:
Cons:
Preferred Design:Second Design ( |
Beta Was this translation helpful? Give feedback.
-
Haven't gave it a lot of thought but have you considered something like |
Beta Was this translation helpful? Give feedback.
-
I hope explicit parameter names can be omitted, so we can keep the expressions simple, like:
and for this reason I don't really like the idea of wrapping arguments in |
Beta Was this translation helpful? Give feedback.
-
Are arguments to builders considered when the type is constructed not using a public constructor? |
Beta Was this translation helpful? Give feedback.
-
Has anybody considered You could maybe even use it to introduce a scope of sorts, to allow the same parameters to be used for nested collections: using(comparer: StringComparer.InvariantCultureIgnoreCase)
[ // the inner dictionaries use the same case-insensitive comparer as the outer one
"en": [ "one": 1, "two": 2, "three": 3, ... ],
"es": [ "uno": 1, "dos": 2, "tres": 3, ...],
"it": [ "uno": 1, "due": 2, "tre": 3, ...],
] |
Beta Was this translation helpful? Give feedback.
-
Note: any solution for arbitrary arguments needs to handle all the facilities we have for arguments today. That means supporting things like ref/out. Supporting named parameters. Supporting params. That's why a syntax that incorporates an argument list is ideal. As that was you get all of that 'for free'. |
Beta Was this translation helpful? Give feedback.
-
What if we allowed the collection expression syntax as an appendage to
|
Beta Was this translation helpful? Give feedback.
-
These all look awful in my opinion. Just call the constructor if arguments need to be passed. If you must use the collection expression because it is the shiny new thing, then it could be one of the parameters of the constructor. I quite like collection expressions and have been using it in my code, but I don't think it needs to fit every scenario. |
Beta Was this translation helpful? Give feedback.
-
With all due respect, I don't find any of those problems compelling at all. The consistency of the proposal being referred to ( To me, options 1 and 2 in that document have the problem of not following existing C# syntax structure (more like python or something), not really being clear and looking like a normal method call (especially in something without syntax coloring) and possibly even conflicting with functions, local functions in particular, with the same name. This is a similar, but maybe less prevalent, problem to using Anyway, if the normal List<string> items = [1, 2, 3] : new (capacity: 32);
List<string> items = [1, 2, 3] : (capacity: 32); // same, just no `new` if that is considered redundant To me, that aligns with constructor chaining, which actually seems pretty similar, at least conceptually, to what we are doing here: "Construct a thing with this stuff, but before that, it needs to be initialized with this other stuff." |
Beta Was this translation helpful? Give feedback.
-
What was the argument against extending object intializers with a kvp kind? new(comparer) {
"k": v
} I don't think allowing spread there would be too bad either, actually I would love to see spreads work with anonymous types: new {
e.Id,
.. e.User
} That comes in handy in efcore projections, for example. |
Beta Was this translation helpful? Give feedback.
-
That doesn't help an the existing collection expr types. It also flights against the goal of just having a unified syntax for all collection types.
This is already an anonymous type. These syntaxes are also not congruent with collection patterns. |
Beta Was this translation helpful? Give feedback.
-
I thought I had made another proposal, but it looks like I either didn't submit it or it was deleted (though I see no indication of that and I would think there would be). Hopefully the original really isn't there and I'm not just missing it. So, to attempt to recreate it: I think this discussion introduces and then leaves out the problem of object initializers with collection expressions. If the goal is to allow constructor arguments (apparently no constructor is ever called, but I don't understand that) with collection expressions, then is the idea that if one wants to use object initialization to set a property on the collection then they are just out of luck? So maybe if object initializers and collection expressions could be used together and constructor arguments (or whatever they actually are) could be passed into the object initialization then it might help in both cases. List<int> aList = new()
{
with(capacity: 32)
} I initially proposed the object initialization could come after the collection expression, but after seeing comments about how that is problematic, it could just be the other way around. List<int> aList = // new could be "gone" because we don't like it and it isn't really needed.
{
with(capacity: 32)
} [1, 2, 3];
// or on one line
List<int> anotherList = { with(capacity: 32) } [1, 2, 3]; Or if we don't like things to be outside, then something like this: List<int> aList =
{
with(capacity: 32),
[1, 2, 3]
}; And the compiler just knows that the bare collection expression inside the object initialization means the list is going to get initialized with that. Another possibility is to just get rid of the List<int> aList =
{
capacity: 32,
[1, 2, 3] // bare collection would use collection expression initialization (and apparently not a parameterless constructor...?)
};
List<int> anotherList =
{
collection: [1, 2, 3],
Capacity = 32
};
List<int> moreList =
{
[1, 2, 3], // bare collection would use collection expression initialization (and apparently not a parameterless constructor...?)
Capacity = 32
};
// if a constructor were being called then this would cause an error, but I've been told no constructors are called when creating the list, so maybe this would also be valid.
List<int> tooMuchList =
{
collection: [1, 2, 3],
capacity: 32
}; Still another proposal would be that maybe collection constructors, if they are in fact actually being called, could have an implicit collection argument, similar to named parameters for List<int> aList = new(capacity: 32, [1, 2, 3]); And then you could just use object initialization below it like you can normally, but the compiler knows you also want the collection built from the expression. And if the constructor just takes a collection anyway, then the collection expression would just get passed into that so that the collection can do whatever it needs to do with it (which might also solve a problem with some of the other proposals where you could pass a collection in as one of these arguments but then also tell the compiler to build a collection for you). Both of these proposals would help with situations like in certain frameworks where the objects are containers, and therefore collections of something, but are also objects "first" that have properties other than those they inherit/implement from collections. An example is Maui and its views. In a situation like that you have to choose between object initialization and collection initialization, and then if your needs change you'll have to switch to the other. |
Beta Was this translation helpful? Give feedback.
-
Actually, speaking of setting properties on collections, it may be better to go with ControlList c = [args(capacity: 5); init { RegisterControlsWithContainer = true }; new Button(), new Label(), new TextBox()];
c.Add(new Button()); |
Beta Was this translation helpful? Give feedback.
-
Collection Expression Arguments.
Summary
Introduce a way for users to create collection expressions (e.g.
[a, b, c]
) while also passing along arguments to the respective creation construction for that collection.An example strawman syntax for this would be:
Beta Was this translation helpful? Give feedback.
All reactions