- "Sorry, I had a meeting run over" "Unacceptable" "Yeah, that never happens here"
- "Do it British style. allouws"
We started today with a timeboxed conversation to allow the implementation work on ref struct
in generics to proceed with parsing work, and hopefully not need to
redo much work later when the LDM makes more complete decisions. We looked at a couple different syntax proposals for how the anti-constraint of allowing ref struct
s
could work:
where T : allows ref struct
- Put a new keyword inside the existingwhere
clause.allows T : ref struct
- Add a new clause that can go beside awhere
clause.
The crux of which form to choose comes down to the question of what we think the existing form actually means: is it only narrowing information about T
, or is more
generally "information about T
". If we think it's the former, then a separate clause for widening T
would be appropriate; if the latter, then we can include widening
information in the allows
. After some quick debate, we lean very heavily towards the latter view. We also think that it will simplify reading and understanding method
definitions, as the user won't have to keep track of multiple sets of information about T
.
We then looked at what keyword to use. We have two real options that were suggested: either allow
, or allows
. This choice was much murkier: does the constraint section
reflect the author saying "I allow T to be a ref struct", or is it reflecting the user reading a constraint and saying "This allows T to be a ref struct". We have a slight
lean towards the latter, but ran out of time in the box before coming to a definitive solution. The major parsing choice is the constraint form, while the keyword choice
is fairly minor and easily changed later before shipping, so we called it in favor of allows
for now, and will revisit later.
We will go with the "put the anti-constraint in the where
clause" form, and we will use the keyword allows
for now.
Finally, we looked at some of the consequences of our previous decision with how the impact a few
real-world types in WinForms and WPF. These are very old collection types; their design predates many of our modern collection designs. Specifically, they implement
System.Collections.IList
, and not any generic version. However, they try to add a bit of type safety by hiding IList.Add(object)
, and instead expose strongly-typed
Add
methods that take the specific type they care about. For example, ListView.ColumnHeaderCollection
exposes an Add(ColumnHeader)
method. However, because it
does not implement IList<T>
, its iteration type is object
; this means, for the purposes of determining whether a collection expression conversion exists, we look
specifically for the Add(object)
method and don't find it. We have a few ideas to address this:
- Remove the restriction we added previously for
IEnumerable
cases for non-params
scenarios. - Do nothing and leave the rules as they are. These collections are:
- Not modified, and collection expressions simply do not work for them.
- Updated to include
IEnumerable<T>
implementations, giving them a more specific iteration type.
- Allow looking for
Add
methods that take a subtype of the iteration type. - Use the strongly-typed indexer of such types to narrow the iteration type.
- Include explicit
Add
implementations in the search for theAdd(object)
method for the purposes of conversion.
Complicating this discussion is a further wrinkle: these collections often have other Add
methods that take other types that can effectively be converted to the
effective iteration type of the collection. For example, ListView.ColumnHeaderCollection
has an Add(string)
method that constructs a ColumnHeader
out of the string
for ease of use. Some of the solutions we're looking at here wouldn't solve that scenario: 1 would work, as would 3, and possibly 5, but 4 would not. 2 likely would, but
we also think it extremely unlikely that we'd be able to do 2.2, specifically adding an implicit conversion from string
to CollectionHeader
. Conceptually, there is
an implicit conversion there; that's what the various Add
methods are simulating. But adding a new implicit user-defined conversion to a type is always a risk, particularly
a type that has been around as long as some of these WinForms and WPF types, and we think it's extremely unlikely that 2.2 would ever happen because of this.
One important note for all of these options around loosening the requirements is that this would be a divergence from what we want to work for params
scenarios; because
we're very concerned about the effect of nested overload resolution on looking for unbounded Add
methods on determining the applicability of expanded params
methods,
we don't have any plans to adjust the behavior for that feature. It will be an unfortunate difference in behavior, but we think it's a necessary one to avoid making overload
resolution even more complex than it already is. This means that if we did option 1 or 3, there would be a difference in behavior. 4 or 5 may be able to apply to params
, but
we'd need to consider it before confirming that it will.
We did not reach any conclusions on this topic today. We want the working group to more fully explore these options and come back with examples of where they will work, where they will fall over, and what the impact would be to various scenarios.