C# - Iteration works without explicit IEnumerable [duplicate] - c#

This question already has answers here:
Duck typing in the C# compiler
(3 answers)
Closed 5 years ago.
the foreach in the following C# code works, even if I doesn't do Days:IEnumerable. So this looks like a kind of duck typing. Why is this possible? I thought Days:IEnumerable is obligatory.
using System;
using System.Collections;
using System.Collections.Generic;
namespace ConsoleApplication1
{
// works even if I does not
// Days:IEnumerable
public class Days
{
string[] tage = { "mon", "tue", "wed", "thu", "fri" };
public IEnumerator GetEnumerator()
{
for (int i = 0; i < tage.Length; i++)
yield return tage[i];
}
}
class Program
{
static void Main(string[] args)
{
Days daylist = new Days();
foreach (string s in daylist) { Console.WriteLine(s); }
}
}
}

According to the C# Language Specification, section 8.8.4 "The foreach statement" (https://msdn.microsoft.com/en-us/library/aa664754(v=vs.71).aspx)
The type of the expression of a foreach statement must be a collection type (as defined below) [...]
[...] A type C is said to be a collection type if it implements the System.Collections.IEnumerable interface or implements the collection pattern by meeting all of the following criteria:
C contains a public instance method with the signature GetEnumerator() that returns a struct-type, class-type, or interface-type, which is called E in the following text.
E contains a public instance method with the signature MoveNext() and the return type bool.
E contains a public instance property named Current that permits reading the current value. The type of this property is said to be the element type of the collection type.
So, not only your class is not obliged to implement IEnumerable, but also, your GetEnumerator() method is not obliged to return an IEnumerator. All that the returned type has to do is implement methods with the right signatures.

No, you don't need that interface. If you try to do a foreach on a non-enumerable data type, like bool, you get this error:
CS1579: foreach statement cannot operate on variables of type 'bool' because 'bool' does not contain a public definition for 'GetEnumerator'
So the requirement is simple: the class must have a public GetEnumerator method.
Then the next error tells us more:
CS0202: foreach requires that the return type 'void' of 'Program.C.GetEnumerator()' must have a suitable public MoveNext method and public Current property
So this is a valid implementation to use foreach, while it is unlike the call will ever end ;)
public class E
{
public bool MoveNext() { return true; }
public object Current { get { return null; } }
}
public class C
{
public E GetEnumerator()
{
return new E();
}
}

Like you said, this benefits from Duck Typing the interface is not mandatory. See MSDN thorough article on the topic.
https://msdn.microsoft.com/en-us/magazine/mt797654.aspx

Related

Check if a Type derives from a Interface with more than one Generic Argument

I have a question about loading types with reflection. I am attempting to filter the list of types in an Assembly by those which implement an interface with two generic parameters. I don't intend to explicitly tell which types are those generic parameters since I want all classes that implement the interface but whenever I attempt to use typeof(IExample<>) it gets marked as an error. However, it's possible to do it with an interface that only has one generic parameter.
I would really appreciate some help with this! Thanks in advance :)
public interface IExample<T, E>
{
}
This is how my interface would looks like.
And then I currently have to classes that implement it.
public class C
{
}
public class A : IExample<string, C>
{
}
Public class B : IExample<XMLDocument, C>
{
}
You weren't to far off from what I could examine from your question. In order to get the correct generic type, without any generic arguments you need to call typeof(IExample<,>), note that there is a comma!
For the other part of your question on how to get those types you can do something like the following:
public static IEnumerable<Type> GetTypesWithGenericArguments(Assembly assembly, Type implementingInterface)
{
var types = assembly.GetTypes();
// Loop over all Types in the assembly
foreach (var type in types)
{
// Skipping all Types which aren't classes
if (!type.IsClass)
continue;
var implementedInterfaces = type.GetInterfaces();
// Loop over all interfaces the type implements
foreach (var implementedInterface in implementedInterfaces)
{
// Return the type if it one of its interfaces are matching the implementingInterface
if (implementedInterface.IsGenericType && implementedInterface.GetGenericTypeDefinition() == implementingInterface)
{
yield return type;
// You can leave the loop, since you don't need to check the other
// interfaces, since you already found the one you were searching for.
break;
}
}
}
}
Which could be used like that:
public static void Main(string[] args)
{
foreach (var item in GetTypesWithGenericArguments(Assembly.GetCallingAssembly(), typeof(IExample<,>)))
{
Console.WriteLine(item.Name);
}
// This would print to the Console:
// A
// B
}

How to dynamically iterate/detect member types in ValueTuple that has null members? [duplicate]

This question already has an answer here:
How to get type of TKey and TValue given a Dictionary<TKey,TValue> type
(1 answer)
Closed 5 years ago.
I'm trying to make use of ValueTuple to concisely enter a list of N types in a generic method's parameter list, and then later iterate that list of types.
However, I'm running into a problem when iterating the types, because the initial Tuple has null members, so calling .GetType() gives me a NullReferenceException.
I realize I could do what I need with an actual list of Type objects, but I'd prefer the very concise syntax that's allowed when creating tuples... and of course because things like this intrigue me :)
public static void IterateTupleMemberTypes<T>() where T : ITuple, new()
{
var tuple = new T();
for (var i = 0; i < tuple.Length; ++i)
{
//First call to GetType() succeeds, first element is an int.
//Second call to GetType() fails (null ref ex), second element is a string.
Console.WriteLine($"Item{i} Type: {tuple[i].GetType()}");
}
}
Usage
public class CustomClass
{
public int ID { get; set; }
}
IterateTupleMemberTypes<(int, string, CustomClass)>();
I think you are mixing data and metadata.
The first call succeed since int is a value type, and default(int) assigns the value to 0, so it works. This won't do for reference types, since these are assigned initially to null.
I believe the proper approach here is, well, reflection - and iterating over the properties of the type, without the need to create an instance (so the new() restriction is redundant).
public static void IterateTupleMemberTypes<T>() where T : ITuple
{
var tupleGenericArguments = typeof(T).GetGenericArguments();
for (var i = 0; i < tupleGenericArguments.Length; ++i)
{
Console.WriteLine($"Item{i} Type: {tupleGenericArguments[i].Name}");
}
}

C# `foreach` behaviour — Clarification?

I've read Eric's article here about foreach enumeration and about the different scenarios where foreach can work
In order to prevent the old C# version to do boxing , the C# team enabled duck typing for foreach to run on a non- Ienumerable collection.(A public GetEnumerator that return something that has public MoveNext and Current property is sufficient(.
So , Eric wrote a sample :
class MyIntegers : IEnumerable
{
public class MyEnumerator : IEnumerator
{
private int index = 0;
object IEnumerator.Current { return this.Current; }
int Current { return index * index; }
public bool MoveNext()
{
if (index > 10) return false;
++index;
return true;
}
}
public MyEnumerator GetEnumerator() { return new MyEnumerator(); }
IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); }
}
But I believe it has some typos (missing get accessor at Current property implementation) which prevent it from compiling (I've already Emailed him).
Anyway here is a working version :
class MyIntegers : IEnumerable
{
public class MyEnumerator : IEnumerator
{
private int index = 0;
public void Reset()
{
throw new NotImplementedException();
}
object IEnumerator.Current {
get { return this.Current; }
}
int Current {
get { return index*index; }
}
public bool MoveNext()
{
if (index > 10) return false;
++index;
return true;
}
}
public MyEnumerator GetEnumerator() { return new MyEnumerator(); }
IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); }
}
Ok.
According to MSDN :
A type C is said to be a collection type if it implements the
System.Collections.IEnumerable interface or implements the
collection pattern by meeting all of the following criteria:
C contains a public instance method with the signature GetEnumerator() that returns a struct-type, class-type, or interface-type, which is called E in the following text.
E contains a public instance method with the signature MoveNext() and the return type bool.
E contains a public instance property named Current that permits reading the current value. The type of this property is said to be the element type of the collection type.
OK. Let's match the docs to Eric's sample
Eric's sample is said to be a collection type because it does implements the System.Collections.IEnumerable interface ( explicitly though). But it is not(!) a collection pattern because of bullet 3 : MyEnumerator does not public instance property named Current.
MSDN says :
If the collection expression is of a type that implements the
collection pattern (as defined above), the expansion of the foreach
statement is:
E enumerator = (collection).GetEnumerator();
try {
while (enumerator.MoveNext()) {
ElementType element = (ElementType)enumerator.Current;
statement;
}
}
finally {
IDisposable disposable = enumerator as System.IDisposable;
if (disposable != null) disposable.Dispose();
}
Otherwise , The collection expression is of a type that implements
System.IEnumerable (!), and the expansion of the foreach statement is:
IEnumerator enumerator =
((System.Collections.IEnumerable)(collection)).GetEnumerator();
try {
while (enumerator.MoveNext()) {
ElementType element = (ElementType)enumerator.Current;
statement;
}
}
finally {
IDisposable disposable = enumerator as System.IDisposable;
if (disposable != null) disposable.Dispose();
}
Question #1
It seems that Eric's sample neither implements the
collection pattern nor System.IEnumerable - so it's not supposed to match any of the condition specified above. So how come I can still iterate it via :
foreach (var element in (new MyIntegers() as IEnumerable ))
{
Console.WriteLine(element);
}
Question #2
Why do I have to mention new MyIntegers() as IEnumerable ? it's already Ienumerable (!!) and even after that , Isn't the compiler is already doing the job by itself via casting :
((System.Collections.IEnumerable)(collection)).GetEnumerator() ?
It is right here :
IEnumerator enumerator =
((System.Collections.IEnumerable)(collection)).GetEnumerator();
try {
while (enumerator.MoveNext()) {
...
So why it still wants me to mention as Ienumerable ?
MyEnumerator does not has the required public methods
Yes it does - or rather, it would if Current were public. All that's required is that it has:
A public, readable Current property
A public MoveNext() method with no type arguments returning bool
The lack of public here was just another typo, basically. As it is, the example doesn't do what it's meant to (prevent boxing). It's using the IEnumerable implementation because you're using new MyIntegers() as IEnumerable - so the expression type is IEnumerable, and it just uses the interface throughout.
You claim that it doesn't implement IEnumerable, (which is System.Collections.IEnumerable, btw) but it does, using explicit interface implementation.
It's easiest to test this sort of thing without implementing IEnumerable at all:
using System;
class BizarreCollection
{
public Enumerator GetEnumerator()
{
return new Enumerator();
}
public class Enumerator
{
private int index = 0;
public bool MoveNext()
{
if (index == 10)
{
return false;
}
index++;
return true;
}
public int Current { get { return index; } }
}
}
class Test
{
static void Main(string[] args)
{
foreach (var item in new BizarreCollection())
{
Console.WriteLine(item);
}
}
}
Now if you make Current private, it won't compile.
The reference to System.IEnumerable on the MSDN is nothing more than a typo in an old language specification, no such interface exists, I believe it should be referring to System.Collections.IEnumerable.
You should really read the language specification of the version of C# you are using, the C# 5.0 language specification is available here.
Some further information about why failing to cast this example results in an error rather than falling back to using System.Collections.IEnumerable:
In the specification for foreach (section 8.8.4) you will see the rules have slightly changed (some steps have been cut for brevity):
Perform member lookup on the type X with identifier GetEnumerator 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 the GetEnumerator 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 identifier Current 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.
The collection type is X, the enumerator type is E, and the element type is the type of the Current property.
So from the first bullet point we would find the public MyEnumerator GetEnumerator(), the second and third bullets go through without error. When we get to the fourth bullet point, no public member called Current is available which results in the error you are seeing without the cast, this exact situation never gives the compiler the opportunity to look for the enumerable interface.
When you explicitly cast your instance to an IEnumerable all of the requirements are satisfied as the type IEnumerable and the associated IEnumerator satisfy all of the requirements.
Also from the documentation:
The above steps, if successful, unambiguously produce a collection type C, enumerator type E and element type T. A foreach statement of the form
foreach (V v in x) embedded-statement
is then expanded to:
{
E e = ((C)(x)).GetEnumerator();
try {
while (e.MoveNext()) {
V v = (V)(T)e.Current;
embedded-statement
}
}
finally {
… // Dispose e
}
}
So given your explicit cast to IEnumerable, you will end up with the following:
C = System.Collections.IEnumerable
x = new MyIntegers() as System.Collections.IEnumerable
E = System.Collections.IEnumerator
T = System.Object
V = System.Object
{
System.Collections.IEnumerator e = ((System.Collections.IEnumerable)(new MyIntegers() as System.Collections.IEnumerable)).GetEnumerator();
try {
while (e.MoveNext()) {
System.Object element = (System.Object)(System.Object)e.Current;
Console.WriteLine(element);
}
}
finally {
System.IDisposable d = e as System.IDisposable;
if (d != null) d.Dispose();
}
}
Which explains why using the cast works.

Creating a class or interface for a complex generic type

I am trying to create an alias for a type of list of list of object. Specifically, I want to shorten all the typing I have to do for this type:
IReadOnlyList<IReadOnlyList<MyObject>>
My attempt is demonstrated here:
using System.Collections.Generic;
namespace MyApp
{
class Program
{
public class MyObject
{
public static IMyCollection GetCollection()
{
var a = new List<MyObject>();
a.Add(new MyObject());
var b = new List<IReadOnlyList<MyObject>>();
b.Add(a.AsReadOnly());
return b.AsReadOnly();
}
}
public interface IMyCollection : IReadOnlyList<IReadOnlyList<MyObject>>
{
}
static void Main(string[] args)
{
var collection = MyObject.GetCollection();
}
}
}
Unfortunately, this won't compile. There error is:
Cannot implicitly convert type
'System.Collections.ObjectModel.ReadOnlyCollection<System.Collections.Generic.IReadOnlyList<MyApp.Program.MyObject>>'
to 'MyApp.Program.IMyCollection'.
An explicit conversion exists (are you missing a cast?)
OK, so I'm close. Perhaps explicitly casting? So I change the return statement in GetCollection to
return (IMyCollection)b.AsReadOnly();
That compiles, albeit with a resharper warning: Suspicious cast: there is no type in the solution which is inherited from both 'System.Collections.ObjectModel.ReadOnlyCollection>' and 'MyApp.Program.IMyCollection'
And at runtime, I get an invalid cast exception: Unable to cast object of type 'System.Collections.ObjectModel.ReadOnlyCollection1[System.Collections.Generic.IReadOnlyList1[MyApp.Program+MyObject]]' to type 'IMyCollection'.
OK, I can accept all that. I'm the last person to ask about stuff like covariance and contravariance and stuff like that. But surely there's a way to define and create an object with a short name to stand in for a really long named datatype.
How can I create a type with a really long name and cast to a type with a really short name?
UPDATE:
A co-worker suggested using a using statement.
using IMyCollection= System.Collections.Generic.IReadOnlyList<System.Collections.Generic.IReadOnlyList<MyApp.Program.MyObject>>;
While that would work, it then becomes necessary to do that in every file that uses IMyCollection. Not exactly what I would consider a solution to my goal.
How badly do you want this?
You can manually implement your own wrapper class.
public interface IMyCollection : IReadOnlyList<IReadOnlyList<MyObject>>
{
}
public class MyCollectionImpl : IMyCollection
{
private readonly IReadOnlyList<IReadOnlyList<MyObject>> _wrappedCollection;
public MyCollectionImpl(IReadOnlyList<IReadOnlyList<MyObject>> wrappedCollection)
{
_wrappedCollection = wrappedCollection;
}
public int Count
{
get
{
return _wrappedCollection.Count;
}
}
public IReadOnlyList<MyObject> this[int index]
{
get
{
return _wrappedCollection[index];
}
}
public IEnumerator<IReadOnlyList<MyObject>> GetEnumerator()
{
return _wrappedCollection.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _wrappedCollection.GetEnumerator();
}
}
Then you simply create an instance of this:
public class MyObject
{
public static IMyCollection GetCollection()
{
var a = new List<MyObject>();
a.Add(new MyObject());
var b = new List<IReadOnlyList<MyObject>>();
b.Add(a.AsReadOnly());
return new MyCollectionImpl(b.AsReadOnly());
}
}
This seems like a lot of extra work, but I would actually consider this a refactoring step.
I believe that the need to pass around types made up of complex set of generic parameters, is actually a bad smell in your code.
Ask yourself, what are you actually using IMyCollection for? Would you be able to add some specialized methods to this interface to make it easier to use?
Once you've created your own MyCollectionImpl class you can slowly add a number of methods to your IMyCollection interface to simplify it's usage. At some point, you might even get to the stage where you can stop exposing the <IReadonlyList<IReadonlyList<MyObject>> interface.
This has nothing to do with covariance. IMyCollection inherits from IReadOnlyList<IReadOnlyList<MyObject>> so you can cast an instance of IMyCollection to IReadOnlyList<IReadOnlyList<MyObject>> but not the other way around.
If you wanna have some custom conversions then you can create a type instead with the short name you want and declare a conversion from IReadOnlyList<IReadOnlyList<MyObject>> to your type using operator overloading. This really seems unnecessary and unusual way to use operator overloading but it's the only way to do what you want to achieve.

Does Class need to implement IEnumerable to use Foreach

This is in C#, I have a class that I am using from some else's DLL. It does not implement IEnumerable but has 2 methods that pass back a IEnumerator. Is there a way I can use a foreach loop on these. The class I am using is sealed.
foreach does not require IEnumerable, contrary to popular belief. All it requires is a method GetEnumerator that returns any object that has the method MoveNext and the get-property Current with the appropriate signatures.
/EDIT: In your case, however, you're out of luck. You can trivially wrap your object, however, to make it enumerable:
class EnumerableWrapper {
private readonly TheObjectType obj;
public EnumerableWrapper(TheObjectType obj) {
this.obj = obj;
}
public IEnumerator<YourType> GetEnumerator() {
return obj.TheMethodReturningTheIEnumerator();
}
}
// Called like this:
foreach (var xyz in new EnumerableWrapper(yourObj))
…;
/EDIT: The following method, proposed by several people, does not work if the method returns an IEnumerator:
foreach (var yz in yourObj.MethodA())
…;
Re: If foreach doesn't require an explicit interface contract, does it find GetEnumerator using reflection?
(I can't comment since I don't have a high enough reputation.)
If you're implying runtime reflection then no. It does it all compiletime, another lesser known fact is that it also check to see if the returned object that might Implement IEnumerator is disposable.
To see this in action consider this (runnable) snippet.
using System;
using System.Collections.Generic;
using System.Text;
namespace ConsoleApplication3
{
class FakeIterator
{
int _count;
public FakeIterator(int count)
{
_count = count;
}
public string Current { get { return "Hello World!"; } }
public bool MoveNext()
{
if(_count-- > 0)
return true;
return false;
}
}
class FakeCollection
{
public FakeIterator GetEnumerator() { return new FakeIterator(3); }
}
class Program
{
static void Main(string[] args)
{
foreach (string value in new FakeCollection())
Console.WriteLine(value);
}
}
}
According to MSDN:
foreach (type identifier in expression) statement
where expression is:
Object collection or array expression.
The type of the collection element
must be convertible to the identifier
type. Do not use an expression that
evaluates to null. Evaluates to a type
that implements IEnumerable or a type
that declares a GetEnumerator method.
In the latter case, GetEnumerator
should either return a type that
implements IEnumerator or declares all
the methods defined in IEnumerator.
Short answer:
You need a class with a method named GetEnumerator, which returns the IEnumerator you already have. Achieve this with a simple wrapper:
class ForeachWrapper
{
private IEnumerator _enumerator;
public ForeachWrapper(Func<IEnumerator> enumerator)
{
_enumerator = enumerator;
}
public IEnumerator GetEnumerator()
{
return _enumerator();
}
}
Usage:
foreach (var element in new ForeachWrapper(x => myClass.MyEnumerator()))
{
...
}
From the C# Language Specification:
The compile-time processing of a
foreach statement first determines the
collection type, enumerator type and
element 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 from X to the
System.Collections.IEnumerable
interface (since System.Array
implements this interface). The
collection type is the
System.Collections.IEnumerable
interface, the enumerator type is the
System.Collections.IEnumerator
interface and the element type is the
element type of the array type X.
Otherwise, determine whether the type X has an appropriate
GetEnumerator method:
Perform member lookup on the type X with identifier GetEnumerator 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 the GetEnumerator 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 identifier Current 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 identifier MoveNext 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 is E, and the element
type is the type of the Current
property.
Otherwise, check for an enumerable interface:
If there is exactly one type T such that there is an implicit
conversion from X to the interface
System.Collections.Generic.IEnumerable<T>,
then the collection type is this
interface, the enumerator type is the
interface
System.Collections.Generic.IEnumerator<T>,
and the element type is T.
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 the
System.Collections.IEnumerable
interface, then the collection type is
this interface, the enumerator type is
the interface
System.Collections.IEnumerator, and
the element type is object.
Otherwise, an error is produced and no further steps are taken.
Not strictly. As long as the class has the required GetEnumerator, MoveNext, Reset, and Current members, it will work with foreach
No, you don't and you don't even need an GetEnumerator method, e.g.:
class Counter
{
public IEnumerable<int> Count(int max)
{
int i = 0;
while (i <= max)
{
yield return i;
i++;
}
yield break;
}
}
which is called this way:
Counter cnt = new Counter();
foreach (var i in cnt.Count(6))
{
Console.WriteLine(i);
}
You could always wrap it, and as an aside to be "foreachable" you only need to have a method called "GetEnumerator" with the proper signature.
class EnumerableAdapter
{
ExternalSillyClass _target;
public EnumerableAdapter(ExternalSillyClass target)
{
_target = target;
}
public IEnumerable GetEnumerator(){ return _target.SomeMethodThatGivesAnEnumerator(); }
}
Given class X with methods A and B that both return IEnumerable, you could use a foreach on the class like this:
foreach (object y in X.A())
{
//...
}
// or
foreach (object y in X.B())
{
//...
}
Presumably the meaning for the enumerables returned by A and B are well-defined.
#Brian: Not sure you try to loop over the value return from method call or the class itself,
If what you want is the class then by make it an array you can use with foreach.
For a class to be usable with foeach all it needs to do is have a public method that returns and IEnumerator named GetEnumerator(), that's it:
Take the following class, it doesn't implement IEnumerable or IEnumerator :
public class Foo
{
private int[] _someInts = { 1, 2, 3, 4, 5, 6 };
public IEnumerator GetEnumerator()
{
foreach (var item in _someInts)
{
yield return item;
}
}
}
alternatively the GetEnumerator() method could be written:
public IEnumerator GetEnumerator()
{
return _someInts.GetEnumerator();
}
When used in a foreach ( Note that the no wrapper is used, just a class instance ):
foreach (int item in new Foo())
{
Console.Write("{0,2}",item);
}
prints:
1 2 3 4 5 6
The type only requires to have a public/non-static/non-generic/parameterless method named GetEnumerator which should return something that has a public MoveNext method and a public Current property. As I recollect Mr Eric Lippert somewhere, this was designed so as to accommodate pre generic era for both type safety and boxing related performance issues in case of value types.
For instance this works:
class Test
{
public SomethingEnumerator GetEnumerator()
{
}
}
class SomethingEnumerator
{
public Something Current //could return anything
{
get { }
}
public bool MoveNext()
{
}
}
//now you can call
foreach (Something thing in new Test()) //type safe
{
}
This is then translated by the compiler to:
var enumerator = new Test().GetEnumerator();
try {
Something element; //pre C# 5
while (enumerator.MoveNext()) {
Something element; //post C# 5
element = (Something)enumerator.Current; //the cast!
statement;
}
}
finally {
IDisposable disposable = enumerator as System.IDisposable;
if (disposable != null) disposable.Dispose();
}
From 8.8.4 section of the spec.
Something worth noting is the enumerator precedence involved - it goes like if you have a public GetEnumerator method, then that is the default choice of foreach irrespective of who is implementing it. For example:
class Test : IEnumerable<int>
{
public SomethingEnumerator GetEnumerator()
{
//this one is called
}
IEnumerator<int> IEnumerable<int>.GetEnumerator()
{
}
}
(If you don't have a public implementation (ie only explicit implementation), then precedence goes like IEnumerator<T> > IEnumerator.)

Categories