- Notifications
You must be signed in to change notification settings - Fork1.1k
Feedback on Collection Expressions#7666
-
C# 12 introduces collection expressions:https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-12#collection-expressions What else would you like to see and what are the important scenarios to you? Dictionaries? Support for |
BetaWas this translation helpful?Give feedback.
All reactions
👍 17
Replies: 15 comments 83 replies
-
I'd love to be able to use collection expressions to type JSON literals. For example,https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/use-dom has this sample code: varforecastObject=newJsonObject{["Date"]=newDateTime(2019,8,1),["Temperature"]=25,["Summary"]="Hot",["DatesAvailable"]=newJsonArray(newDateTime(2019,8,1),newDateTime(2019,8,2)),["TemperatureRanges"]=newJsonObject{["Cold"]=newJsonObject{["High"]=20,["Low"]=-10}},["SummaryWords"]=newJsonArray("Cool","Windy","Humid")}; but it would be nice to write it as something like this: JsonObjectforecastObject=["Date":newDateTime(2019,8,1),"Temperature":25,"Summary":"Hot","DatesAvailable":[newDateTime(2019,8,1),newDateTime(2019,8,2)],"TemperatureRanges":["Cold":["High":20,"Low":-10]],"SummaryWords":["Cool","Windy","Humid"]]; |
BetaWas this translation helpful?Give feedback.
All reactions
-
Use JSON here as an example. JsonNode isn't directly creatable, but it should expose creation fromboth JsonArray and JsonObject, since it's the element type of them. This can introduce ambiguous for empty collection. Of course |
BetaWas this translation helpful?Give feedback.
All reactions
-
We might consider a syntactic form to state that the result must be a dictionary-type. But it's low on our list of cares currently. |
BetaWas this translation helpful?Give feedback.
All reactions
-
Isn't this an API request for the maintainer of the |
BetaWas this translation helpful?Give feedback.
All reactions
-
The important part of this request isn't that the This capability will be very useful with JSON APIs. |
BetaWas this translation helpful?Give feedback.
All reactions
-
@GabeSchaffer that's the thing - if I'm not mistaken, as designed (proposed) today, C# should support this kind of expression initialization - given the correct public surface area of the types in question. |
BetaWas this translation helpful?Give feedback.
All reactions
👍 1
-
Yes and yes to dictionaries and Whilst this will introduce a third way of initialising a dictionary, it will be super-nice syntax and is definitely worth doing in my view. Regarding |
BetaWas this translation helpful?Give feedback.
All reactions
👍 7
-
I view forward-looking as valuable because I and probably many others have this very common pattern everywhere: varlist=newList<BlaBla>();// so tired of typing this outvarlist=[];// bingowhile(...){// ... stuff ...list.Add(newBlaBla{ ...});}// use list for something - return, set property, etc. |
BetaWas this translation helpful?Give feedback.
All reactions
-
IMO the rules would end up being ridiculously complicated and run into the same problems of "best common type" that have basically made this inference impractical anywhere else it's been suggested in the language. |
BetaWas this translation helpful?Give feedback.
All reactions
-
Fwiw, I have a proposal for doing this. I think this is space worth looking into. I think we can find a sweet spot that is not too complex, while also supporting the majority of cases |
BetaWas this translation helpful?Give feedback.
All reactions
-
Would that change the conversation around other forms of deferred inference that had been effectively rejected in the past? |
BetaWas this translation helpful?Give feedback.
All reactions
-
Very likely yes. My proposal is not specific to collection expressions. That said, we might use collections to try out this space before broadening to other types. |
BetaWas this translation helpful?Give feedback.
All reactions
-
I want a way to omit null values from my collection expression. Ie: var Values = [?x, ?..y]; |
BetaWas this translation helpful?Give feedback.
All reactions
-
For conditionally including single items in a collection expression, how about a suffix |
BetaWas this translation helpful?Give feedback.
All reactions
-
@sab39 Def an option. I'm also a fan of |
BetaWas this translation helpful?Give feedback.
All reactions
👍 1
-
@CyrusNajmabadi I don't want to get too far into bikeshedding a feature that's speculative in the first place (and worth doing no matter what syntax is selected, imo) but I'd like to give my rationale for suggesting |
BetaWas this translation helpful?Give feedback.
All reactions
-
@sab39 understood. And we'd def consider that. IMO, i'd still prefer |
BetaWas this translation helpful?Give feedback.
All reactions
👍 1❤️ 1
-
I prefer it on the left as I feel like it has symmetry with ?. |
BetaWas this translation helpful?Give feedback.
All reactions
-
I'd like to see support for Before: protectedstaticreadonlyISet<string>SupportedImageFileMimeTypes=newHashSet<string>(){"image/jpeg","image/png","image/webp"}; After: protectedstaticreadonlyISet<string>SupportedImageFileMimeTypes=["image/jpeg","image/png","image/webp"]; |
BetaWas this translation helpful?Give feedback.
All reactions
👍 1
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
@stephentoub how about a flag on the attribute for "static readonly"? Some types get 2 attributes. Then the language can use one of the two strategies where applicable depending on the site |
BetaWas this translation helpful?Give feedback.
All reactions
-
I woudl be fine with unsorted (as nothing about teh core interfaces indicates it is sorted). I think if we wanted sorted-sets to be something to target, perhaps having an |
BetaWas this translation helpful?Give feedback.
All reactions
👍 1
-
Speaking of sorted collection interfaces; what about |
BetaWas this translation helpful?Give feedback.
All reactions
-
The only interfaces the language supports are those that array implements. Any other interfaces need the `[CollectionBuilder] attribute added to them. |
BetaWas this translation helpful?Give feedback.
All reactions
-
IOrderedEnumerable is an implementation detail that out of necessity is part of the public API. It's only purpose is to allow ThenBy to be available only after an OrderBy, as otherwise the ThenBy is meaningless. We wouldn't want collection expressions to be able to create an IOrderedEnumerable as doing so would go against its purpose.
That's adding language and library complexity for something that can more easily be solved just by someone expressing exactly what they want, e.g. typing the field as |
BetaWas this translation helpful?Give feedback.
All reactions
👍 1
-
Extension method support (separate from natural type) to allow things like: publicstaticImmutableArray<T>AsImmutableArray<T>(thisImmutableArray<T>items)=>items;// consumed elsewhere asvarmyImmutableArray=[1,2,3].AsImmutableArray(); |
BetaWas this translation helpful?Give feedback.
All reactions
👍 4
-
|
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
@sab39 Looks like we had similar thoughts.#7666 (comment) |
BetaWas this translation helpful?Give feedback.
All reactions
❤️ 1
-
I think we might still want the |
BetaWas this translation helpful?Give feedback.
All reactions
-
Could it, though? Wouldn't the dictionary type have to implement |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
|
BetaWas this translation helpful?Give feedback.
All reactions
❤️ 1
-
Actually, I'm wrong, due to a change in direction that happened very late:https://github.com/dotnet/csharplang/blob/main/meetings/2023/LDM-2023-10-02.md#collection-expressions The implicit conversion is necessary; collection expressions which construct with Add calls donot support all the scenarios that collection initializers do. |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
ℹ️ Until there is actual support for dictionary literals, the following syntax does work (sharplab) for C# 12 so long as you're willing to declare an extension method: Dictionary<int,string>dict1=[new(1,"one"),new(2,"two")];Dictionary<int,string>dict2=[..dict1,new(3,"three")];publicstaticclassExtensions{publicstaticvoidAdd<TKey,TValue>(thisIDictionary<TKey,TValue>dictionary,KeyValuePair<TKey,TValue>entry){dictionary.Add(entry);}} (Don't worry, this isn't recursive. It binds to |
BetaWas this translation helpful?Give feedback.
All reactions
👍 2🎉 1
-
BetaWas this translation helpful?Give feedback.
All reactions
-
@vriesdea Please file an issue at github.com/dotnet/roslyn. Please provide a full repro if you can. Thanks. :) |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
A way to create ILookup would be appreciated (probably by returning an instance of the internal Lookup class). Currently, the only way to create them is using the .ToLookup() extension method. ILookup<int, string> x = [ 1 => ["a", "b", "c"], 2 => ["d"] ]; |
BetaWas this translation helpful?Give feedback.
All reactions
-
Suggestion 1. Integer range expressions within collection expressions, e.g.: int[]array1=[0..n];// n elements from 0 inclusive to n exclusivelong[]array2=[0..10,20..30,100];// 21 elements Here, Suggestion 2 (along with suggestion 1). Collection expressions in foreach(intiin0..n){Something(i);}foreach(longiin0..10,20..30,100){Something(i);} Of course, this would be a "collection expression" only when there are at least two elements or a range. |
BetaWas this translation helpful?Give feedback.
All reactions
👍 3👀 1
-
You can get the first part of suggestion 2 quite easily today with an extension method: publicstaticIEnumerable<int>Each(thisRangerange){if(range.Start.IsFromEnd||range.End.IsFromEnd)thrownewArgumentException("...");returnEnumerable.Range(range.Start.Value,range.End.Value-range.Start.Value);}publicstaticIEnumerator<int>GetEnumerator(thisRangerange)=>range.Each().GetEnumerator();foreach(variin0..n){Something(i);} Yes, that's technically two extension methods - they could easily be combined of course, but int[]array1=[..(0..n).Each()];// (0..n).Each().ToArray() would probably be cleaner herelong[]array2=[..(0..10).Each(), ..(20..30).Each(),100]; Long term, the existing proposal for extensions will let you new interfaces to existing types. At that point you can extend foreach(longiin[..(0..10), ..(20..30),100]){Something(i);} |
BetaWas this translation helpful?Give feedback.
All reactions
-
I'd love to see both of these implemented. There doesn't seem any real interest in the language team in addressing this missing functionality though. @sab39, the problem with using extension methods to work around this feature gap is that And as you say yourself, this only achieves a small part of what could be achieved if the C# language directly supported value ranges, rather than the current index-based ranges. As for shapes, extension everything and all those other much promised utopia features appearing before some AI solution takes over the task of writing code, I'm not holding my breath... |
BetaWas this translation helpful?Give feedback.
All reactions
👎 3
-
This is incorrect. The language team is interested in these cases.
We're currently in the prototyping stage of those features. So it's further along than most other language proposals. |
BetaWas this translation helpful?Give feedback.
All reactions
-
There is one more natural use case—collection expressions in Method(0..10,20..30,100);voidMethod(paramsint[]args){Something(args);} The way |
BetaWas this translation helpful?Give feedback.
All reactions
-
What's the rationale here? Like can a collection expression be assigned to string in any circumstance? |
BetaWas this translation helpful?Give feedback.
All reactions
-
@TahirAhmadov having string be a supported target type does not mean overloads like this would not work.
i've def wanted this. but it's mostly just about consistency. in the language, a string is already a collection of chars (and already works with linq/foreach/etc. uniformly like other collections). Having it work as a target just means it works fine in the other direction. Note: this is not to say that it is equivalent in priority to a char[]/span target. |
BetaWas this translation helpful?Give feedback.
All reactions
👍 2
-
Do you mean that the behavior@Tinister experienced (according to his comment) is an preview bug and that it will work as expected later? |
BetaWas this translation helpful?Give feedback.
All reactions
-
it is the expected behavior in c#12. we explicitly carved it out so we can make it work in c# 13. |
BetaWas this translation helpful?Give feedback.
All reactions
-
Why is |
BetaWas this translation helpful?Give feedback.
All reactions
-
@Tinister one is |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
There seems to be a lack of a default type now, consider the following scenariopublicclassA{publicstaticimplicitoperatorA(int[]source)=>thrownewNotImplementedException();//...}// Suppose there's a method accepting an argument of type AvoidM(Aa)=>thrownewNotImplementedException();// Current attempt at calling the methodvoidM2(){M([1,2,3]);// Produces error CS1503}// The correct way to call it currentlyint[]x1={1,2,3};M(x1);// This works correctly |
BetaWas this translation helpful?Give feedback.
All reactions
-
Default type is intentionally not implemented for C# 12. The team is still deciding the correct approach for C# 13. |
BetaWas this translation helpful?Give feedback.
All reactions
😄 1
-
Note; this is not really a case of a default type. This is more a case of the language not "seeing through" implicit operators. It's soemthing we could consider, and then you could use collection exprs anywhere where there was an implicit operator and the source-type itself was something that could be constructed by collection exprs. |
BetaWas this translation helpful?Give feedback.
All reactions
-
Have you considered adding features like 'index functions'? If we have such functions, we only need to provide the expression of this function and the length to construct an array in this form before compilation. However, after compilation, it can be an array with a fixed length and each element is also determined. |
BetaWas this translation helpful?Give feedback.
All reactions
-
That feels like a feature much better suited to a helper method or via LINQ, especially since it would be something that could only be evaluated at compile-time in the most limited of circumstances. Collection literals aren't intended to be another form of comprehension syntax. |
BetaWas this translation helpful?Give feedback.
All reactions
-
/*Of course, we can go further like Python. We can replace the position of "length" with another collection, even if their types are not the same, as long as the index function can calculate it.*/int[]x1=[i=>2*i,5];float[]x2=[i=>(i+3)/2,x1];//ok |
BetaWas this translation helpful?Give feedback.
All reactions
-
This is already valid: int[]x1=[..fromiinEnumerable.Range(5)select2*i]; |
BetaWas this translation helpful?Give feedback.
All reactions
-
See#7634 |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
/*However, compared to the LINQ expression you provided, my solution obviously has more room for optimization and performance advantages. Because the context variable 'i' here is directly defined by the array expression environment, and yours is provided by the LINQ extension method, and the extension method is used multiple times from the 'from' clause expansion to 'select' selection, etc. Moreover, I think such syntax is supported by C# after the collection expansion operator, and I don't see anything special about the separate issue you provided; and the index function is a mapping based on array indexing, with clear semantics.*/int[]x1=[..fromiinEnumerable.Range(5)select2*i];// This can hardly be considered a new proposal because since the expansion operation of '..' no matter what, you can at least do this:int[]x1=[..(fromiinEnumerable.Range(5)select2*i).ToArray()];/*So actually no matter how you write it, as long as it satisfies two points, one, it is an expression, and two, it returns an array. After satisfying these two points, using the expansion operator can make it be constructed into an array; so the existence value of this index function expression I mentioned may not be that high. Although it looks more concise and clear, it is indeed not something completely new that cannot be expressed with old technology. This is also worth discussing and thinking about, to see if there is a need for the index function to exist.*/ |
BetaWas this translation helpful?Give feedback.
All reactions
-
I'd love to be able to use collection expression directly in
yields Currently known workarounds:
or
Notes:
|
BetaWas this translation helpful?Give feedback.
All reactions
-
Thanks for the feedback, and great news, work is underway for this! |
BetaWas this translation helpful?Give feedback.
All reactions
🎉 2
-
BetaWas this translation helpful?Give feedback.
All reactions
-
The question is not aboutelement type, but thecontainer type. There are different type of collections in .NET. Array, list and spans are all having unique advantages. We didn't have a conclusion to prefer any one. |
BetaWas this translation helpful?Give feedback.
All reactions
👍 2