日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產(chǎn)品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
.NET教程:.NET面試題之IEnumerable(二)-創(chuàng)新互聯(lián)

.NET教程,這篇文章還是接著上文介紹的第二部分!多的不說,直接獻上內容!

創(chuàng)新互聯(lián)建站主營陸豐網(wǎng)站建設的網(wǎng)絡公司,主營網(wǎng)站建設方案,成都App定制開發(fā),陸豐h5微信小程序開發(fā)搭建,陸豐網(wǎng)站營銷推廣歡迎陸豐等地區(qū)企業(yè)咨詢

    使用yield關鍵字實現(xiàn)方法GetEnumerator

如果iterator本身有實現(xiàn)IEnumerator接口(本例就是一個數(shù)組),則可以有更容易的方法:

public IEnumerator GetEnumerator()

{

return _people.GetEnumerator();

}

注意,這個方法沒有Foreach的存在,所以如果你改用for循環(huán)去迭代這個集合,你得自己去呼叫MoveNext,然后獲得集合的下一個成員。而且會出現(xiàn)一個問題,就是你無法知道集合的大小(IEnumerable沒有Count方法,只有IEnumerable才有)。

此時,可以做個試驗,如果我們知道一個集合有3個成員,故意迭代多幾次,比如迭代10次,那么當集合已經(jīng)到達尾部時,將會拋出InvalidOperationException異常。

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();

//Will 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();

}

}

使用yield關鍵字配合return,編譯器將會自動實現(xiàn)繼承IEnumerator接口的類和上面的三個方法。而且,當for循環(huán)遍歷超過集合大小時,不會拋出異常,Current會一直停留在集合的最后一個元素。

public IEnumerator GetEnumerator()

{

foreach (Person p in _people)

yield return p;

}

如果我們在yield的上面加一句:

public IEnumerator GetEnumerator()

{

foreach (var p in _persons)

{

Console.WriteLine("test");

yield return p;

}

}

我們會發(fā)現(xiàn)test只會打印三次。后面因為已經(jīng)沒有新的元素了,yield也就不執(zhí)行了,整個Foreach循環(huán)將什么都不做。

yield的延遲執(zhí)行特性 – 本質上是一個狀態(tài)機

關鍵字yield只有當真正需要迭代并取到元素時才會執(zhí)行。yield是一個語法糖,它的本質是為我們實現(xiàn)IEnumerator接口。

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";

}

在上面的例子中,盡管我們呼叫了GetItems方法,先打印出來的句子卻是主函數(shù)中的句子。這是因為只有在ToList時,才真正開始進行迭代,獲得迭代的成員。我們可以使用ILSpy察看編譯后的程序集的內容,并在View -> Option的Decompiler中,關閉所有的功能對勾(否則你將仍然只看到一些yield),然后檢查Program類型,我們會發(fā)現(xiàn)編譯器幫我們實現(xiàn)的MoveNext函數(shù),實際上是一個switch。第一個yield之前的所有代碼,統(tǒng)統(tǒng)被放在了第一個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;

}

如果某個yield之前有其他代碼,它會自動包容到它最近的后續(xù)的yield的“統(tǒng)治范圍”:

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";

}

它的編譯結果也是可以預測的:

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;

這也就解釋了為什么第一個打印出來的句子在主函數(shù)中,因為所有不是yield的代碼統(tǒng)統(tǒng)都被yield吃掉了,并成為狀態(tài)機的一部分。而在迭×××始之前,代碼是無法運行到switch分支的。

令人矚目的是,編譯器沒有實現(xiàn)reset方法,這意味著不支持多次迭代:

void IEnumerator.Reset()

{

throw new NotSupportedException();

}

yield只返回,不賦值

下面這個例子。不過我認為Artech大大分析的不是很好,我給出自己的解釋。

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 are 3 members in vectors, all with X and Y = 4

foreach (var vector in vectors)

{

//But this iterate will 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);

}

}

我們進行調試,并將斷點設置在第二次迭代之前,此時,我們發(fā)現(xiàn)vector的值確實變成4了,但第二次迭代之后,值又回去了,好像被改回來了一樣。但實際上,并沒有改任何值,yield只是老老實實的吐出了新的三個vector而已。Yield就像一個血汗工廠,不停的制造新值,不會修改任何值。

從編譯后的代碼我們發(fā)現(xiàn),只要我們通過foreach迭代一個IEnumerable,我們就會跑到GetVectors方法中,而每次運行GetVectors方法,yield都只會返回全新的三個值為(1,1),(2,2)和(3,3)的vector,仿佛第一次迭代完全沒有運行過一樣。原文中,也有實驗證明了vector創(chuàng)建了六次,實際上每次迭代都會創(chuàng)建三個新的vector。

解決這個問題的方法是將IEnumerable轉為其子類型例如List或數(shù)組。

在迭代的過程中改變集合的狀態(tài)

foreach迭代時不能直接更改集合成員的值,但如果集合成員是類或者結構,則可以更改其屬性或字段的值。不能在為集合刪除或者增加成員,這會出現(xiàn)運行時異常。For循環(huán)則可以。

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);

}

IEnumerable的缺點

IEnumerable功能有限,不能插入和刪除。

訪問IEnumerable只能通過迭代,不能使用索引器。迭代顯然是非線程安全的,每次IEnumerable都會生成新的IEnumerator,從而形成多個互相不影響的迭代過程。

在迭代時,只能前進不能后退。新的迭代不會記得之前迭代后值的任何變化。

另外有需要云服務器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內外云服務器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務器、裸金屬服務器、高防服務器、香港服務器、美國服務器、虛擬主機、免備案服務器”等云主機租用服務以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應用場景需求。


分享標題:.NET教程:.NET面試題之IEnumerable(二)-創(chuàng)新互聯(lián)
URL標題:http://www.dlmjj.cn/article/coheig.html