C # Set-Enumeration)

Source: Internet
Author: User
Tags mscorlib

There are many types of sets in the computer field, from simple data structures such as arrays and linked lists to complex data structures such as red/black trees and hash tables. Although the internal implementation of these data structures is quite different from that of external features, the content of traversal sets is indeed a common requirement .. NET Framework uses the IEnumerable and IEnumerator interfaces to traverse the set.

Non-Generic Generic Remarks
IEnumerator IEnumerator <T>  
IEnumerable IEnumerable <T> Traversal only
ICollection ICollection <T> Traversal: collects statistics on the elements of a set.
IDictionary
IList
IDictionary <TKey, TValue>
IList <T>
More functions

 

IEnumerable and IEnumerator

The IEnumerator interface defines the traversal Protocol. In this protocol, elements in the set are traversed in the forward mode. Its statement is as follows:

 

MoveNext moves the current element or pointer to the next position. If there is no element in the next position, false is returned. Current returns the element at the Current value position. Before getting the first element of a set, you must call the MoveNext method-this is also applicable to empty sets. Reset method, which moves to the initial position and allows the set to be traversed again. Too many Resets are designed for COM interoperability: Avoid calling this method directly as much as possible because it is not widely supported (it is unnecessary to call this method directly, because it is easier to create a new list instance ).

Collections generally do not implement enumerators. On the contrary, they provide enumerators through the IEnurable interface.

 

By defining a single return enumerator method, the IEnumerable interface provides more flexibility, so that the logic of the traversal set of each implementation class can be the same. This means that users of each set can create their own methods to traverse the set without affecting each other. IEnumerable can be considered as IEnumeratorProvider, which is an interface that must be implemented by all collection classes.

The following code demonstrates how to use IEnumerable and IEnumerator:

 s = IEnumerator rator =+  ( c + );

Generally, the GetEnumerator method is rarely called to obtain the IEnumerator interface. This is because C # provides the foreach syntax (after the foreach syntax is compiled, GetEnumerator is automatically called to traverse the set ), this makes the code more concise.

 

IEnumerable <T> and IEnumerator <T>

The Generic interfaces of IEnumerator and IEnumerable are defined as follows:

  IEnumerator< T>  IEnumerable< T> IEnumerator<T>

The Current and GetEnumerator interfaces of Generic increase the type security of the IEnumerable <T> and IEnumerator <T> interfaces, avoiding the binning of value types and making the set more convenient for users. Note that the number type implements the IEnumerable <T> interface by default.

It is precisely because of the implementation of the Type-safe interface that the method Test2 (arr) will report an error during compilation:

  Main([] arr =  [] { , ,     Test2(arr);   ( i +   Test2(IEnumerable<> ( i + 

Please note that Array implements the IEnumerable <T> interface by default, so it must also implement the IEnumerable interface. Although char [] cannot be converted to IEnumrable <int>, it can be converted to IEnumeable. Therefore, Test1 can be compiled, while Test2 cannot be compiled (type conversion failure error)

For collection classes, exposing IEnumerable <T> is a standard practice. The IEnumerable interface must be displayed to hide non-Generic IEnumerable. Then, call GetEnumerator to obtain IEnumerator <T>. But sometimes, to be compatible with non-Generic sets, we can not follow this rule. The best example is the array set. The array must return IEnumerator of a non-generic to avoid conflicts with earlier code. In this case, to obtain IEnumerator <T>, you must first convert the array display to the Generic interface, and then obtain:

[] arr =  [] { , ,  rator = ((IEnumerable<>)arr).GetEnumerator();

Fortunately, you rarely need to write such code, thanks to the foreach statement.

 

IEnumerable <T> and IDisposable

IEnumerator <T> inherits IDisposable. This allows the enumerator to have resource references, such as database connections, to ensure that these resources are released after the traversal is complete. The foreach statement recognizes this feature. For example, the following foreach statement

IList<> chars = List<>(){, ,  ( c 

The compiled code is:

   static   Main([] args)         [mscorlib]System.Collections.Generic.IEnumerator`<!>  [mscorlib]System.Collections.Generic.IEnumerable`<  <          [mscorlib]System.Console::Write(            

Therefore, if you implement the IEnumable <T> interface, When you execute foreach, it is converted to calling GetEnumerator <T>. After the traversal is complete, release IEnumerator <T>.

 

List Interfaces

When one or more of the following conditions are met, you must implement IEnumerable or IEnumerable <T>

To implement IEnumerable/IEnumerable <T>, you must provide an enumerator. You can implement the following three methods:

  • If the class contains another set, you must return the enumerator of the contained set.
  • Use yield return
  • Implementation of instantiating IEnumerator/IEnumerator <T>

1) instance IEnumerator/IEnumerator <T>

The enumerator that returns another set is the GetEnumerator that calls the internal set. However, this only happens in simple scenarios. In such scenarios, the elements in the internal set have already met the needs. Another more flexible method is to generate an iterator using the yield return statement. Iteraotr is a C # Language Feature used to assist in the production of sets. Likewise, foreach can be used with iterator to traverse sets. An iterator automatically processes the implementation of IEnumerable and IEnumerator. The following is a simple example.

 [] data ={ , ,  ( i  

Note that GetEnumerator does not return an enumerator at all. Dependent on the statement after parsing yield return, the compiler compiles a hidden embedded enumerator class, refactor GetEnumerator to implement instantiation, and finally return the class. Iteration is not only powerful but also simple.

Through the IL code, we can see that an embedded enumerator class is indeed produced.

 

Based on the above Code, we made some changes to MyCollecton so that it not only implements IEnumerable, but also implements IEnumerable <T>

  MyCollection : IEnumerable<>[] data ={ , ,  IEnumerator<> ( i   

Because IEnumerable <T> inherits IEnumerable, we must implement GetEnumerator of generic and GetEnumerator of non-generic. According to the standard practice, we have implemented the GetEnumerator of Generic. Therefore, for non-Generic GetEnumerator, we can directly call the GetEnumerator of Generic, because IEnumerable <T> inherits IEnumerbale.

The corresponding IL code is as follows: (Note the IEnumerator <Int32> interface implemented by the compiler, instead of the IEnumerator <Object> Interface)

2) use yield return to return IEnumerable <T>

The MyCollection class we created can be used as the basic implementation of complex collection classes. However, if you do not need to implement IEnumerable <T>, you should be able to use the yield return statement to implement an IEnumerable <T> instead of writing a class like MyCollection. That is to say, you can migrate the iteration logic to a method that returns IEnumerable <T>, and then let the compiler do the rest for you.

  Main(( i  IEnumerable<>[] data = { , ,  ( i  

Corresponding IL code

From the IL code, we can see that the compiler also produces an internal class, which implements the IEnumerator <Int32> interface.

3) if the class contains another set, you must return the enumerator of the contained set.

The last implementation method is to compile a class to directly implement the IEnumerator interface. In fact, this is what the compiler did before. In practice, you do not need to do this.

First, we will implement non-Generic IEnumerator.

 [] data ={ , ,   Enumerator( .collection == -  {   (index < collection.data.Length-++   = -

Then, based on the above code, we can implement the IEnumerator of Generic.

  MyCollection : IEnumerable<Int32>[] data = { , ,      IEnumerator<Int32>  Enumerator(  Enumerator : IEnumerator<Int32>.collection == - implement IEnumerator<T>              {    (index < collection.data.Length - ++   = -                 { 

The IEnumerator of the Generic version is more efficient than the IEnumberator of the non-Generic version, because it does not need to convert int to object, thus reducing the overhead of packing. Let's take a look at the corresponding IL code:

Obviously, we can see that the manually created Enumerator is the same as the Enumerator generated by the compiler.

 

In addition, when we use the second method, if we have multiple IEnumerable <T> methods, the compiler will generate multiple classes that implement IEnumerator <T>.

  Main( ( i  ( i  IEnumerable<Int32>[] collection = { , , , ,  ( i   IEnumerable<Int32>[] collection = { , , , ,  ( i (i%== 

The corresponding IL code shows two internal IEnumerator <T> classes.

 

The following code generates only one IEnumerator <T> class.

  Main( ( i  ( i  IEnumerable<Int32> IEnumerable<Int32> GetDetails(  IEnumerable<Int32> GetDetails( isOdd = [] collection = { , , , ,  index =  ( i  (isOdd && i %  ==   (! ++

Similarly, the following code generates only one IEnumerator <T> class.

 IEnumerable<Int32> ( i   IEnumerable<Int32> ( i  GetDetails( 

 

Therefore, we can find that when implementing IEnumerable, especially when there are multiple implementations, we need to minimize the number of classes that the compiler generates IEnumerator. I guess internally, the compiler should determine the number of IEnumerator classes based on the iterator of different yield return. In my sample code, when two IEnumerator classes are generated, the iterator of yield return of GetSomeIntegers and GetSomeOdds are different. When an IEnumerator class is generated, they all point to the iterator corresponding to the yield return of GetDetails.

 

Finally, let's take a look.IEnumeratorAndIterator

There is no clear distinction between the two on the Internet. Maybe I have confused the two concepts that should not be confused. The following is my opinion. If it is incorrect, please correct me:

1) Implementing IEnumerator is used to implement IEnumerable, which is associated with the GetEnumerator method, so that foreach can be used. Once a class determines the traversal (MoveNext) method, this is the only way to traverse the set .. By default, IEnumerator in most collections in the NET Framework traverses the set in the forward read-only mode.

2) Iterator is used to traverse a set. There are multiple implementation methods. The only requirement is to return IEnumerator <T>. In a sense, Iterator is IEnumerator. The difference between the two is that once the former is determined, it can only traverse the set in this way and then return an IEnumerator. The latter can traverse the set in multiple ways in multiple methods and then return different IEnumerator. (I think the difference between the two is similar to that between IComparable and IComparer ).

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.