You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
§13.9.5 The foreach statement of the current draft-v9 says that when processing foreach, and after not finding a GetEnumerable method (in practice, this happens with explicit interface implementation), we do the following:
If among all the types Tᵢ for which there is an implicit conversion from X to IEnumerable<Tᵢ>, there is a unique type T such that T is not dynamic and for all the other Tᵢ there is an implicit conversion from IEnumerable<T> to IEnumerable<Tᵢ>, then the collection type is the interface IEnumerable<T>, the enumerator type is the interface IEnumerator<T>, and the iteration type is T.
I think relying on implicit conversions here is problematic. Also, it's not what Roslyn actually does, resulting in differences between the specification and the implementations.
In particular:
The specification allows implementing IEnumerable<T> multiple times and directly using such type in foreach, when one of the type parameters inherits from the other:
foreach(var x innew MyCollection()){}classMyCollection:IEnumerable<string>,IEnumerable<object>{
IEnumerator IEnumerable.GetEnumerator()=>thrownew NotImplementedException();IEnumerator<string>IEnumerable<string>.GetEnumerator()=>thrownew NotImplementedException();IEnumerator<object>IEnumerable<object>.GetEnumerator()=>thrownew NotImplementedException();}
According to the spec, the unique type T here should be string, since there is an implicit covariant conversion from IEnumerable<string> to IEnumerable<object>. The compiler does not allow this code:
error CS1640:: foreach statement cannot operate on variables of type 'MyCollection' because it implements multiple instantiations of 'IEnumerable'; try casting to a specific interface instantiation
The specification results in the wrong iteration type for a type that implements IEnumerable<dynamic>:
foreach(var x innewMyCollection<dynamic>()){}classMyCollection<T>:IEnumerable<T>{IEnumerator<T>IEnumerable<T>.GetEnumerator()=>thrownew NotImplementedException();
IEnumerator IEnumerable.GetEnumerator()=>thrownew NotImplementedException();}
According to the spec, the unique type T here should be object, since the type is implicitly convertible to both IEnumerable<object> and IEnumerable<dynamic>, but dynamic is explicitly forbidden. The compiler determines the iteration type to be dynamic, as expected.
It seems to me that this could be resolved by following the compiler, and specifying this in terms of the interfaces actually implemented by the type in question, not based on implicit conversions. But I don't know whether that's viable.
The text was updated successfully, but these errors were encountered:
§13.9.5 The foreach statement of the current draft-v9 says that when processing
foreach
, and after not finding aGetEnumerable
method (in practice, this happens with explicit interface implementation), we do the following:I think relying on implicit conversions here is problematic. Also, it's not what Roslyn actually does, resulting in differences between the specification and the implementations.
In particular:
The specification allows implementing
IEnumerable<T>
multiple times and directly using such type inforeach
, when one of the type parameters inherits from the other:According to the spec, the unique type
T
here should bestring
, since there is an implicit covariant conversion fromIEnumerable<string>
toIEnumerable<object>
. The compiler does not allow this code:The specification results in the wrong iteration type for a type that implements
IEnumerable<dynamic>
:According to the spec, the unique type
T
here should beobject
, since the type is implicitly convertible to bothIEnumerable<object>
andIEnumerable<dynamic>
, butdynamic
is explicitly forbidden. The compiler determines the iteration type to bedynamic
, as expected.It seems to me that this could be resolved by following the compiler, and specifying this in terms of the interfaces actually implemented by the type in question, not based on implicit conversions. But I don't know whether that's viable.
The text was updated successfully, but these errors were encountered: