I have created an object and implemented the interface IEnumerable<Options>
.
If I try to loop through my object it is working fine but the variables aren't casted to a Options
but object
.
public class MyClass : IEnumerable<Options>
{
// I just cut the non-relevant code. The real class is over 500 lines ...
public List<Options> options = new List<Options>();
IEnumerator<Options> IEnumerable<Options>.GetEnumerator()
{
return options.GetEnumerator();
}
public IEnumerator GetEnumerator()
{
return options.GetEnumerator();
}
}
How I loop through:
foreach (var option in myClass)
{
// Do something
}
typeof(option)
returns object
. But since I implemented the generic interface I want it to be returned as Options
. It is indeed an Options
object I can explicitly cast it with (Options)option
but it should be automatically casted.
foreach (Options option in myClass)
{
// I want it to be casted automatically when using 'var'
}
CodePudding user response:
Quoting from the C# language reference, 12.9.5 The foreach statement:
The compile-time processing of a
foreach
statement first determines the collection type, enumerator type and iteration type of the expression. This determination proceeds as follows:
- If the type
X
of expression is an array type then there is an implicit reference conversion fromX
to theIEnumerable
interface (sinceSystem.Array
implements this interface). The collection type is theIEnumerable
interface, the enumerator type is theIEnumerator
interface and the iteration type is the element type of the array type X.- If the type
X
of expression isdynamic
then there is an implicit conversion from expression to theIEnumerable
interface (§10.2.10). The collection type is theIEnumerable
interface and the enumerator type is theIEnumerator
interface. If thevar
identifier is given as the local_variable_type then the iteration type isdynamic
, otherwise it isobject
.- Otherwise, determine whether the type
X
has an appropriateGetEnumerator
method:
- Perform member lookup on the type
X
with identifierGetEnumerator
and no type arguments. If the member lookup does not produce a match, or it produces an ambiguity, or produces a match that is not a method group, check for an enumerable interface as described below. It is recommended that a warning be issued if member lookup produces anything except a method group or no match.- Perform overload resolution using the resulting method group and an empty argument list. If overload resolution results in no applicable methods, results in an ambiguity, or results in a single best method but that method is either static or not public, check for an enumerable interface as described below. It is recommended that a warning be issued if overload resolution produces anything except an unambiguous public instance method or no applicable methods.
- If the return type
E
of theGetEnumerator
method is not a class, struct or interface type, an error is produced and no further steps are taken.- Member lookup is performed on
E
with the identifierCurrent
and no type arguments. If the member lookup produces no match, the result is an error, or the result is anything except a public instance property that permits reading, an error is produced and no further steps are taken.- Member lookup is performed on
E
with the identifierMoveNext
and no type arguments. If the member lookup produces no match, the result is an error, or the result is anything except a method group, an error is produced and no further steps are taken.- Overload resolution is performed on the method group with an empty argument list. If overload resolution results in no applicable methods, results in an ambiguity, or results in a single best method but that method is either static or not public, or its return type is not bool, an error is produced and no further steps are taken.
- The collection type is
X
, the enumerator type isE
, and the iteration type is the type of theCurrent
property.- Otherwise, check for an enumerable interface:
- If among all the types
Tᵢ
for which there is an implicit conversion fromX
toIEnumerable<Tᵢ>
, there is a unique typeT
such thatT
is not dynamic and for all the otherTᵢ
there is an implicit conversion fromIEnumerable<T>
toIEnumerable<Tᵢ>
, then the collection type is the interfaceIEnumerable<T>
, the enumerator type is the interfaceIEnumerator<T>
, and the iteration type isT
.- Otherwise, if there is more than one such type
T
, then an error is produced and no further steps are taken.- Otherwise, if there is an implicit conversion from
X
to theSystem.Collections.IEnumerable
interface, then the collection type is this interface, the enumerator type is the interfaceSystem.Collections.IEnumerator
, and the iteration type is object.- Otherwise, an error is produced and no further steps are taken.
So in your case the compiler found a public GetEnumerator
method in the MyClass
, which happened to be a method that returns an IEnumerator
, and was happy to use this method for the foreach
enumeration because the criteria were satisfied. There was no reason to go a step down and check for an enumerable interface, so the explicitly implemented IEnumerable<Options>.GetEnumerator
method was ignored.
CodePudding user response:
In the foreach
the compiler does not see the generic version of the GetEnumerator
method, because you are implementing it explicitly, and hence without a cast to IEnumerable<Options>
the IEnumerable
implementation is used, yielding object
s.
With your current implementation you'd have to do:
foreach (var option in (IEnumerable<Options>) myClass)
then option
is of type Options
.
You should implement the non-generic version explicitly, and the generic version implicitly. Then it works as you expect:
IEnumerator<Options> IEnumerable<Options>.GetEnumerator()
{
return options.GetEnumerator();
}
public IEnumerator GetEnumerator()
{
return options.GetEnumerator();
}
and the type of var option
will be Options
.
CodePudding user response:
I would try to implement the interface IEnumerable
explicitly, and IEnumerable<T>
publicly
public IEnumerator<Options> GetEnumerator()
{
return options.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
So the default is the generic function, and the non-generic is called only explicitly.