The LINQ Principle |
Written by Mike James | ||||||
Thursday, 25 June 2020 | ||||||
Page 2 of 5
IEnumerableThe reason is that any class offering an enumerator has to implement the IEnumerable interface. This has just a single method GetEnumerator which returns the instance of the class that provides the methods of the IEnumerator. If you think about it for a moment it is obvious that IEnumerable has to be implemented by the data collection class that holds the items to be enumerated so we have to add it to the class definition: class TestCollection: The single method that we have to implement is trivial as the instance of the TestCollection class concerned provides its own enumerators, i.e. it is the enumerator to be returned: IEnumerator IEnumerable.GetEnumerator() Now we can write some code that makes use of our data collection with enumeration. The simplest thing to try out is a foreach loop: TestCollection col = Notice that the iterator o has to be defined as an object. We can use an int in the foreach loop as it supports implicit casting. For example: foreach (int o in col) works perfectly unless you happen to return something that can’t be cast to an int at runtime with the result that an exception is raised. If you want to do better then you need to change the enumeration interfaces to their generic forms – which is what we have to do anyway to use LINQ. Separating the enumeratorBefore moving on it is worth commenting on why we usually implement the enumerator as a separate class. Consider what happens if we try to use the current enumerator in a nested foreach loop? The same enumerator would be returned by GetEnumerator and hence the nested loops would interfere with each other. If you want to write nested loops or allow multiple enumerations to happen concurrently you need to implement the enumerator as a separate class and you need to create an instance of that class each time an enumerator is called for by GetEnumerator. As nesting of queries is very common we need to do the job properly before moving on to consider LINQ. All we need to do is separate out the enumeration methods into a new class. This class has to keep track of where it is in the enumeration and it needs to keep track of which instance of the collection it is enumerating: class TestCollectionEnnumerator:IEnumerator If you create the enumeration class as an inner class of the data collection, i.e. TestCollection, it will have access to all of the data collection’s variables and methods and this makes it easier for the two classes to work together. The only new method we need is a constructor that initialises the enumerator with the current instance of the data collection: public TestCollectionEnnumerator( The other enumeration methods have to now use m_Instance instead of “this” to make sure they work with the correct instance of the data: void IEnumerator.Reset() object IEnumerator.Current
We also need to change the data collection class so that its GetEnumerator actually creates a new instance of the enumerator: class TestCollection:IEnumerable
Notice how the use of “this” makes the final connection between the instance and any number of enumerators that are generated. Now our example code is slightly more complicated but you can use it within nested for each loops and, as we shall see, nested LINQ queries.
|
||||||
Last Updated ( Thursday, 25 June 2020 ) |