Replies: 6 comments
-
Does this mean the |
Beta Was this translation helpful? Give feedback.
-
I would be seriously unhappy if it did. 😆 |
Beta Was this translation helpful? Give feedback.
-
That depends on factors that contribute to the type inference. Being Update: updated. |
Beta Was this translation helpful? Give feedback.
-
Would this proposal allow me to write eg. |
Beta Was this translation helpful? Give feedback.
-
@hickford That's another issue with type inference. See #129. I haven't suggested any changes to type inference with method groups here. Correction: Since the following works, I'd say yes you can do that. void M<T>(Func<T> f) {}
M(Path.GetTempFileName); |
Beta Was this translation helpful? Give feedback.
-
Taking a look at F# spec, it uses a simpler rule for the name resolution, it only fallbacks to the generic type when there's only a single one, so |
Beta Was this translation helpful? Give feedback.
-
Constructor type argument inference
Consider
T
is a name with arity zero.Only if the existing rules are unable to find a constructor to bind, we continue to the next step to retain backward compatibility. In other words, we do so if either of the following conditions are true:
T
does not exist or is inaccessibleT
is (a) not a class or struct (b) not a type parameterT
is static or abstractT
does not have a constructor or struct constraintAfter that, we gather all the types in scope with the same name regardless of the arity (the arity here would be always greater than zero, because we already tried all names with the arity zero). Then we add all accessible constructors to a set of candidates and run an overload resolution pass. For example, considering:
We resolve
new C(...)
just like how we do generic method overload resolution,At the end, if no unfixed type parameters exist then type inference (and the overload resolution) succeeds and we have a single best constructor to be invoked.
Note that "closeness" rules are at play as well, so when we gather generic types, we do so step-by-step for types that are closer to the call-site.
For example, we resolve
new C(42)
in the following code:Just like we resolve
C(42)
in the following:Therefore, it would not be ambiguous.
Collection Initializers
If the type arguments couldn't be inferred, in the presence of collection initializers, we will try to run the same pass on
Add
methods. Sonew List { 1, 2, 3 }
works as expected.Currently, a collection initializer is allowed to call different
Add
overloads based on each element type. To keep the rules simple, we only consider the first element of the initializer when we run the overload resolution pass to resolve the type, and produce error for other elements, if needed.For example, we would fail to infer
C<int>
fornew C { { 1, 2 }, 2 };
here:Because the matching overload for the first element does not fix the type parameter
T
.Obejct Initializers
We won't depend on the object initializers to infer any type argument, so an object initializer is permitted only if all type arguments are already inferred from provided arguments.
Alternatives
When we gather all constructors to the candidate set, there could be conflicting overloads, and if that happened, those should be removed from the set. Alternatively, we could consider "omitted type arguments" to specify the arity without mentioning the type arguments, e.g.
new C<,>()
so we don't have to do a complex name resolution to determine the arity. Just that we will try to infer type arguments from the constructor arguments or the collection initializer, if any.Vartiations
A similar approach could be taken to infer type arguments on static method invocations.
Beta Was this translation helpful? Give feedback.
All reactions