dotnet / csharplang

The official repo for the design of the C# programming language
11.55k stars 1.03k forks source link

Championed: Target-typed implicit array creation expression `new[]` #2701

Open alrz opened 5 years ago

alrz commented 5 years ago

Allow KeyValuePair<string, string>[] array = new[] { new("key1", value1), new("key2", value2) };


With introduction of new() expression (#100), the following:

IEnumerable<KeyValuePair<string, string>> Headers = new[]
{
     new KeyValuePair<string, string>("Foo", foo),
     new KeyValuePair<string, string>("Bar", bar),
}

can be simplified to:

IEnumerable<KeyValuePair<string, string>> Headers = new KeyValuePair<string, string>[]
{
     new("Foo", foo),
     new("Bar", bar),
}

but you still need to repeat the type following the field/property initializer. Closest you can get is something like:

IEnumerable<KeyValuePair<string, string>> Headers = new[]
{
     new KeyValuePair<string, string>("Foo", foo),
     new("Bar", bar),
}

For the sake of completeness, I'd suggest to also make new[] a target-typed expression,

IEnumerable<KeyValuePair<string, string>> Headers = new[]
{
     new("Foo", foo),
     new("Bar", bar),
}

The mechanics of such feature is similar to #2389 or #2460 where there are two source of type inference, the common type and the target-type.

Note: A list/dictionary literal feature would not supersede this proposal because in above examples there's no concrete collection type to infer.

alrz commented 5 years ago

As with how this feature would interact with overload resolution there's some room for tweaks,

void M(List<int> list) {}
void M(IEnumerable<int>  list) {}

If new[] would be convertible to any type just like new(), both this invocations would be ambiguous,

M(new() { }); // could bind to first method
M(new[] { }); // could bind to second method

While this is good for breaking change predictability, it also means that it's easier to introduce one, and it narrows down where the feature can be used overall.

--

For an untyped new[] we could only accept arrays AND array generic interfaces as the target type and yield no conversion otherwise. This would be similar to lambas which are only convertible to delegates.

For an untyped new() we could only accept constructible types or more loosely non-array types instead of any type.

This distinction would eliminate the candidates that are not compatible with the argument from get-go, since we can say if we want to create an array or a non-array type via new[] or new().

lcsondes commented 4 years ago

Would it not work better to relax the array initializer syntax to allow {{a,b},{c,d}} instead of {new(a,b),new(c,d)} for the KeyValuePair example?

lcsondes commented 3 years ago

I would like if this also included int[] x = new[1];, not just new[]{...}.

CyrusNajmabadi commented 2 years ago

I think this proposal can effectively be subsumed into collection literals.

You would be able to do the following:

KeyValuePair<string, string>[] array = [new("key1", value1), new("key2", value2)];

That solves the array part. KVP is also very special to collection literals so the above could also be shortened to:

KeyValuePair<string, string>[] array = ["key1": value1, "key2": value2];

Note: A list/dictionary literal feature would not supersede this proposal because in above examples there's no concrete collection type to infer.

We support collection/dictionary literals even the absence of a concrete collection type :)

alrz commented 2 years ago

Right, I think it will also work with IEnumerable but only as long as there is a compatible natural type.

IEnumerable<T> x = [value]; // ok; but the target-type (T) won't affect type inference in any ways here.
IEnumerable<T> x = []; // error; no natural type? (is there a default natural type when the list is empty?)
IEnumerable<bool?> x = [true, null]; // error; the most-common-type is `bool` and `List<bool>` fails to assign.

Relates to https://github.com/dotnet/roslyn/issues/37950

CyrusNajmabadi commented 2 years ago

All of the above would be legal @alrz

alrz commented 2 years ago

Great! Though I didn't find anything about a null-aware common type.. does that last example work with var?

CyrusNajmabadi commented 2 years ago

The last example will work with 'var' if 'best common type' is willing to infer bool? for true and null. So we would be gated on that.

RikkiGibson commented 2 years ago

if 'best common type' is willing to infer bool? for true and null.

Unfortunately, I think it is not. SharpLab

CyrusNajmabadi commented 2 years ago

@RikkiGibson right, that's what i'm saying. We're gated on that being a supported concept for c#. the same applies to existing collections today.

333fred commented 2 years ago

When we didn't do that in C# 8, we accepted that it would likely never be done. https://github.com/dotnet/csharplang/issues/881

audacode commented 1 year ago

I opened a discussion thread here: https://github.com/dotnet/csharplang/discussions/7175 But closed it once I found this issue..

In short: It is great if this type of syntax will work in C# 12:

Student[] students = [new("John"),new("Jane")];

But we still should have a goal of implementing this feature on the original syntax for the sake of language consistency.

Student[] studentsB = new[] { new("John"), new("Jane") }; // This would ideally work as well.