Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!
There's a PR for target typed new
expressions
M(new (1));
We generally like it, and have been interested in a feature like this for a while, but we're a bit concerned about the ways in which use of it can be brittle. In the code above, adding another constructor to a type (which one of the overloads takes) can now cause an ambiguity, or cause a different overload to be picked.
It's roughly as "bad" as adding another implicit conversion.
We could consider restrictions. We could do it only in initializers, which is a common request. But that's still a harsh restriction. We would probably soon be back to considering the general version of the feature.
We could warn (or have an analyzer warn) if there is more than one possible target parameter type among overloads corresponding to a target-typed new
argument.
We could also take a similar approach as we do to out vars. There we don't even take the parameter type into account for betterness purposes. We're ambiguous if two overloads differ only on the type of an out parameter, regardless of whether one is "better" than the other. If we do this similarly, then folks wouldn't easily get silent changes when overloads are added: instead they'd get ambiguity errors, and would be forced to put in a type name, or otherwise disambiguate.
We would like to pursue this feature. Let's schedule a design meeting to make sure we have the design ironed out. We like the more restrictive approach to overload resolution, similar to out vars.
This is a convenience. However, it risks a syntactic conflict with other potential futures, especially "non-local returns" (allowing a lambda to return from its enclosing method) and "block expressions" (allowing statements inside expressions). While we can imagine syntaxes for those that do not conflict, we don't want to limit the design space for them at this point, at least not for a feature that is merely "nice to have".
Also, while we've been talking about this in analogy with throw expressions, that isn't quite right. throw
is a dynamic effect, whereas return
, break
and continue
are statically bound control transfers with a specific target.
We're grateful for the pull request, but do not want to go forward with the feature at this point.
Just like foreach it will look for an interface or a pattern. Should the pattern require Task<T>
, or be pattern-based all the way down?
Pattern all the way down. Same as with await
.
foreach
doesn't allow extension methods for GetEnumerator
etc. We would like to change that, but there are obscure potential breaking changes there. Should foreach await
allow extension methods?
Let's allow, and work to allow for synchronous foreach
as well.
We don't do iterator lambdas today, and it's low priority to add it, though it's not really harmful.
We won't do it for async iterators either, until such time as we decide to do it for the synchronous ones.
In synchronous iterators, the yield
keyword is what makes it an iterator. The same for async iterators: the combo of the async
modifier and the presence of the yield
keyword makes it an async iterator. Whether you actually await is at most the subject of a warning, just as withe other async methods, and has no other bearing.
Async methods no longer have to return Task
and Task<T>
, but can follow a pattern. We don't currently do a similar thing for iterators, but we should think about it, also for async iterators.
Block it. For synchronous foreach we resort to the nongeneric IEnumerable
, but there is no nongeneric IAsyncEnumerable
, and there won't be.