. NET Tutorial: IEnumerable of questions on. NET Face (ii)

Source: Internet
Author: User

. NET tutorial, this article is followed by the second part of the above introduction! Many do not say, direct content!

Using the Yield keyword implementation method GetEnumerator

If the iterator itself implements the IEnumerator interface (in this case an array), there is an easier way to do it:

Public IEnumerator GetEnumerator ()

{

Return _people. GetEnumerator ();

}

Note that this method does not have a foreach, so if you use a for loop to iterate over the set, you have to call MoveNext yourself and get the next member of the collection. And there is a problem that you cannot know the size of the collection (IEnumerable has no Count method, only IEnumerable).

At this point, you can do an experiment, if we know that a set has 3 members, deliberately iterative several times, such as iteration 10 times, then when the collection has reached the tail, will throw InvalidOperationException exception.

Class Program

{

static void Main (string[] args)

{

person P1 = new Person ("1");

person P2 = new Person ("2");

Person P3 = new Person ("3");

People p = new people (New PERSON[3]{P1, p2, p3});

var enumerator = P.getenumerator ();

would throw InvalidOperationException

for (int i = 0; i < 5; i++)

{

Enumerator. MoveNext ();

if (enumerator. current = null)

{

var currentp = (person) Enumerator. Current;

Console.WriteLine ("Current is {0}", currentp.name);

}

}

Console.readkey ();

}

}

public class Person

{

public string Name {get; set;}

Public person (string name)

{

name = name;

}

}

public class People:ienumerable

{

Private ReadOnly person[] _persons;

Public people (person[] persons)

{

_persons = persons;

}

Public IEnumerator GetEnumerator ()

{

Return _persons. GetEnumerator ();

}

}

Using the yield keyword with return, the compiler will automatically implement classes that inherit the IEnumerator interface and the three methods above. Also, when a for loop iterates over the collection size, no exception is thrown, and current stays at the last element of the collection.

Public IEnumerator GetEnumerator ()

{

foreach (person p in _people)

Yield return p;

}

If we add a sentence to the yield:

Public IEnumerator GetEnumerator ()

{

foreach (var p in _persons)

{

Console.WriteLine ("Test");

Yield return p;

}

}

We will find that test will only print three times. Since there is no new element in the back, yield is not executed, and the entire foreach loop will do nothing.

Yield deferred execution characteristics – essentially a state machine

The keyword yield executes only if it really needs to iterate and fetch the element. Yield is a syntactic sugar, and its essence is to implement the IEnumerator interface for us.

static void Main (string[] args)

{

IEnumerable items = GetItems ();

Console.WriteLine ("Begin to iterate the collection.");

var ret = items. ToList ();

Console.readkey ();

}

Static IEnumerable GetItems ()

{

Console.WriteLine ("Begin to invoke GetItems ()");

Yield return "1";

Yield return "2";

Yield return "3";

}

In the above example, although we call the GetItems method, the first printed sentence is the sentence in the main function. This is because only when you are ToList, do you really start iterating and get the members of the iteration. We can use Ilspy to look at the contents of the compiled assembly and, in the Decompiler of view, option, turn off all the tick marks (otherwise you will still see only some yield) and then check the program type. We will find that the compiler helps us implement the MoveNext function, which is actually a switch. All the code before the first yield is put in the first case.

BOOL Ienumerator.movenext ()

{

BOOL result;

Switch (this.<>1__state)

{

Case 0:

This.<>1__state =-1;

Console.WriteLine ("Begin to invoke GetItems ()");

This.<>2__current = "1";

This.<>1__state = 1;

result = true;

return result;

Case 1:

This.<>1__state =-1;

This.<>2__current = "2";

This.<>1__state = 2;

result = true;

return result;

Case 2:

This.<>1__state =-1;

This.<>2__current = "3";

This.<>1__state = 3;

result = true;

return result;

Case 3:

This.<>1__state =-1;

Break

}

result = false;

return result;

}

If a yield has other code before it, it automatically embraces the "dominant range" of its nearest subsequent yield:

Static IEnumerable GetItems ()

{

Console.WriteLine ("Begin to invoke GetItems ()");

Console.WriteLine ("Begin to invoke GetItems ()");

Yield return "1";

Console.WriteLine ("Begin to invoke GetItems ()");

Yield return "2";

Console.WriteLine ("Begin to invoke GetItems ()");

Console.WriteLine ("Begin to invoke GetItems ()");

Console.WriteLine ("Begin to invoke GetItems ()");

Yield return "3";

}

The results of this compilation are also predictable:

Case 0:

This.<>1__state =-1;

Console.WriteLine ("Begin to invoke GetItems ()");

Console.WriteLine ("Begin to invoke GetItems ()");

This.<>2__current = "1";

This.<>1__state = 1;

result = true;

return result;

Case 1:

This.<>1__state =-1;

Console.WriteLine ("Begin to invoke GetItems ()");

This.<>2__current = "2";

This.<>1__state = 2;

result = true;

return result;

Case 2:

This.<>1__state =-1;

Console.WriteLine ("Begin to invoke GetItems ()");

Console.WriteLine ("Begin to invoke GetItems ()");

Console.WriteLine ("Begin to invoke GetItems ()");

This.<>2__current = "3";

This.<>1__state = 3;

result = true;

return result;

Case 3:

This.<>1__state =-1;

Break

This explains why the first printed sentence is in the main function, because all code that is not yield is eaten by yield and becomes part of the state machine. The code cannot be run to the switch branch until the iteration begins.

It is striking that the compiler does not implement the Reset method, which means that multiple iterations are not supported:

void Ienumerator.reset ()

{

throw new NotSupportedException ();

}

Yield returns only, not assigned value

The following example. But I think Artech's analysis is not very good, I give my own explanation.

Class Program

{

static void Main (string[] args)

{

IEnumerable vectors = getvectors ();

Begin to call Getvectors

foreach (var vector in vectors)

{

Vector. X = 4;

Vector. Y = 4;

}

Before this iterate, there is 3 members in vectors, all with X and Y = 4

foreach (var vector in vectors)

{

But this iterate would change the value of X and Y back to 1/2/3

Console.WriteLine (vector);

}

}

Static IEnumerable getvectors ()

{

Yield return new Vector (1, 1);

Yield return new Vector (2, 3);

Yield return new Vector (3, 3);

}

}

public class Vector

{

Public double X {get; set;}

Public double Y {get; set;}

Public Vector (double x, double y)

{

This. x = x;

This. y = y;

}

public override string ToString ()

{

return string. Format ("X = {0}, Y = {1}", this. X, this. Y);

}

}

We debug and set the breakpoint before the second iteration, when we find that the value of the vector actually turns 4, but after the second iteration, the value goes back, as if it were changed back. But in fact, did not change any value, yield only honestly spit out the new three vector only. Yield is like a sweatshop that keeps creating new values without modifying any values.

From the compiled code we found that as soon as we iterate through a IEnumerable, we will run into the Getvectors method, and each time the Getvectors method is run, yield will only return a completely new three values of (), (2,2) and ( 3,3) vector, as if the first iteration had not run at all. In the original text, there are also experiments to prove that the vector was created six times, in fact, each iteration will create three new vectors.

The solution to this problem is to convert IEnumerable to its subtypes such as list or array.

Changing the state of a collection during an iteration

A foreach iteration cannot directly change the value of a collection member, but if the collection member is a class or struct, you can change the value of its property or field. You cannot delete or add members to a collection, which can cause a run-time exception. The For loop is available.

var vectors = getvectors (). ToList ();

foreach (var vector in vectors)

{

if (vector. X = = 1)

Error

Vectors. Remove (vector);

This is OK

Vector. X = 99;

Console.WriteLine (vector);

}

Disadvantages of IEnumerable

IEnumerable features are limited and cannot be inserted or deleted.

Access IEnumerable can only be iterated through and cannot use indexers. Iterations are clearly non-thread-safe, and each time IEnumerable generates new IEnumerator, creating multiple iterative processes that do not affect each other.

In the iteration, you can only forward and not back. The new iteration will not remember any changes to the values of the previous iterations.

. NET Tutorial: IEnumerable of questions on. NET Face (ii)

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.