.NET教程,今天給大家介紹的是:.NET 面試題之IEnumerable ,這是在面試的時(shí)候可能會(huì)碰到的一道題目,這道題的注解分為了兩個(gè)部分,這一篇是第一部分!
成都創(chuàng)新互聯(lián)公司網(wǎng)站建設(shè)由有經(jīng)驗(yàn)的網(wǎng)站設(shè)計(jì)師、開(kāi)發(fā)人員和項(xiàng)目經(jīng)理組成的專(zhuān)業(yè)建站團(tuán)隊(duì),負(fù)責(zé)網(wǎng)站視覺(jué)設(shè)計(jì)、用戶(hù)體驗(yàn)優(yōu)化、交互設(shè)計(jì)和前端開(kāi)發(fā)等方面的工作,以確保網(wǎng)站外觀(guān)精美、成都做網(wǎng)站、網(wǎng)站制作易于使用并且具有良好的響應(yīng)性。
什么是IEnumerable?
IEnumerable及IEnumerable的泛型版本IEnumerable是一個(gè)接口,它只含有一個(gè)方法GetEnumerator。Enumerable這個(gè)靜態(tài)類(lèi)型含有很多擴(kuò)展方法,其擴(kuò)展的目標(biāo)是IEnumerable。
實(shí)現(xiàn)了這個(gè)接口的類(lèi)可以使用Foreach關(guān)鍵字進(jìn)行迭代(迭代的意思是對(duì)于一個(gè)集合,可以逐一取出元素并遍歷之)。
實(shí)現(xiàn)這個(gè)接口必須實(shí)現(xiàn)方法GetEnumerator。
如何實(shí)現(xiàn)一個(gè)繼承IEnumerable的類(lèi)型?
實(shí)現(xiàn)一個(gè)繼承IEnumerable的類(lèi)型等同于實(shí)現(xiàn)方法GetEnumerator。想知道如何實(shí)現(xiàn)方法GetEnumerator,不妨思考下實(shí)現(xiàn)了GetEnumerator之后的類(lèi)型在Foreach之下的行為:
可以獲得第一個(gè)或當(dāng)前成員
可以移動(dòng)到下一個(gè)成員
可以在集合沒(méi)有下一個(gè)成員時(shí)退出循環(huán)。
假設(shè)我們有一個(gè)很簡(jiǎn)單的Person類(lèi)(例子來(lái)自MSDN):
public class Person
{
public Person(string fName, string lName)
{
FirstName = fName;
LastName = lName;
}
public string FirstName;
public string LastName;
}
然后我們想構(gòu)造一個(gè)沒(méi)有實(shí)現(xiàn)IEnumerable的類(lèi)型,其儲(chǔ)存多個(gè)Person,然后再對(duì)這個(gè)類(lèi)型實(shí)現(xiàn)IEnumerable。
這個(gè)類(lèi)型實(shí)際上的作用就相當(dāng)于Person[]或List,但我們不能使用它們,因?yàn)樗鼈円呀?jīng)實(shí)現(xiàn)了IEnumerable,故我們構(gòu)造一個(gè)People類(lèi),模擬很多人(People是Person的復(fù)數(shù)形式)。
這個(gè)類(lèi)型允許我們傳入一組Person的數(shù)組。所以它應(yīng)當(dāng)有一個(gè)Person[]類(lèi)型的成員,和一個(gè)構(gòu)造函數(shù),其可以接受一個(gè)Person[],然后將Person[]類(lèi)型的成員填充進(jìn)去作為初始化。
//People類(lèi)就是Person類(lèi)的集合
//但我們不能用List或者Person[],因?yàn)樗麄兌紝?shí)現(xiàn)了IEnumerable
//我們要自己實(shí)現(xiàn)一個(gè)IEnumerable
//所以請(qǐng)將People類(lèi)想象成List或者類(lèi)似物
public class People : IEnumerable
{
private readonly Person[] _people;
public People(Person[] pArray)
{
//構(gòu)造一個(gè)Person的集合
_people = new Person[pArray.Length];
for (var i = 0; i < pArray.Length; i++)
{
_people[i] = pArray[i];
}
}
//實(shí)現(xiàn)IEnumerable需要實(shí)現(xiàn)GetEnumerator方法
public IEnumerator GetEnumerator()
{
throw new NotImplementedException();
}
}
我們的主函數(shù)應(yīng)當(dāng)是:
public static void Main(string[] args)
{
//新的Person數(shù)組
Person[] peopleArray =
{
new Person("John", "Smith"),
new Person("Jim", "Johnson"),
new Person("Sue", "Rabon"),
};
//People類(lèi)實(shí)現(xiàn)了IEnumerable
var peopleList = new People(peopleArray);
//枚舉時(shí)先訪(fǎng)問(wèn)MoveNext方法
//如果返回真,則獲得當(dāng)前對(duì)象,返回假,就退出此次枚舉
foreach (Person p in peopleList)
Console.WriteLine(p.FirstName + " " + p.LastName);
}
但現(xiàn)在我們的程序不能運(yùn)行,因?yàn)槲覀冞€沒(méi)實(shí)現(xiàn)GetEnumerator方法。
實(shí)現(xiàn)方法GetEnumerator
GetEnumerator方法需要一個(gè)IEnumerator類(lèi)型的返回值,這個(gè)類(lèi)型是一個(gè)接口,所以我們不能這樣寫(xiě):
return new IEnumerator();
因?yàn)槲覀儾荒軐?shí)例化一個(gè)接口。我們必須再寫(xiě)一個(gè)類(lèi)PeopleEnumerator,它繼承這個(gè)接口,實(shí)現(xiàn)這個(gè)接口所有的成員:Current屬性,兩個(gè)方法MoveNext和Reset。于是我們的代碼又變成了這樣:
//實(shí)現(xiàn)IEnumerable需要實(shí)現(xiàn)GetEnumerator方法
public IEnumerator GetEnumerator()
{
return new PeopleEnumerator();
}
在類(lèi)型中:
public class PeopleEnumerator : IEnumerator
{
public bool MoveNext()
{
throw new NotImplementedException();
}
public void Reset()
{
throw new NotImplementedException();
}
public object Current { get; }
}
現(xiàn)在問(wèn)題轉(zhuǎn)移為實(shí)現(xiàn)兩個(gè)方法,它們的功能看上去一目了然:一個(gè)負(fù)責(zé)將集合中Current向后移動(dòng)一位,一個(gè)則將Current初始化為0。
我們可以查看IEnumerator元數(shù)據(jù),其解釋十分清楚:
Enumerator代表一個(gè)類(lèi)似箭頭的東西,它指向這個(gè)集合當(dāng)前迭代指向的成員
IEnumerator接口類(lèi)型對(duì)非泛型集合實(shí)現(xiàn)迭代
Current表示集合當(dāng)前的元素,我們需要用它僅有的get方法取得當(dāng)前元素
MoveNext方法根據(jù)Enumerator是否可以繼續(xù)向后移動(dòng)返回真或假
Reset方法將Enumerator移到集合的開(kāi)頭
通過(guò)上面的文字,我們可以理解GetEnumerator方法,就是獲得當(dāng)前Enumerator指向的成員。我們引入一個(gè)整型變量position來(lái)記錄當(dāng)前的位置,并且先試著寫(xiě)下:
public class PeopleEnumerator : IEnumerator
{
public Person[] _peoples;
public object Current { get; }
//當(dāng)前位置
public int position;
//構(gòu)造函數(shù)接受外部一個(gè)集合并初始化自己內(nèi)部的屬性_peoples
public PeopleEnumerator(Person[] peoples)
{
_peoples = peoples;
}
//如果沒(méi)到集合的尾部就移動(dòng)position,返回一個(gè)bool
public bool MoveNext()
{
if (position < _peoples.Length)
{
position++;
return true;
}
return false;
}
public void Reset()
{
position = 0;
}
}
這看上去好像沒(méi)問(wèn)題,但一執(zhí)行之后卻發(fā)現(xiàn):
當(dāng)執(zhí)行到MoveNext方法時(shí),position會(huì)先增加1,這導(dǎo)致第一個(gè)元素(在位置0)會(huì)被遺漏,故position的初始值應(yīng)當(dāng)為-1而不是0
當(dāng)前位置變量position顯然應(yīng)該是私有的
需要編寫(xiě)Current屬性的get方法取出當(dāng)前位置(position)上的集合成員
通過(guò)不斷的調(diào)試,最后完整的實(shí)現(xiàn)應(yīng)當(dāng)是:
public class PeopleEnumerator : IEnumerator
{
public Person[] People;
//每次運(yùn)行到MoveNext或Reset時(shí),利用get方法自動(dòng)更新當(dāng)前位置指向的對(duì)象
object IEnumerator.Current
{
get
{
try
{
//當(dāng)前位置的對(duì)象
return People[_position];
}
catch (IndexOutOfRangeException)
{
throw new InvalidOperationException();
}
}
}
//當(dāng)前位置
private int _position = -1;
public PeopleEnumerator(Person[] people)
{
People = people;
}
//當(dāng)程序運(yùn)行到foreach循環(huán)中的in時(shí),就調(diào)用這個(gè)方法獲得下一個(gè)person對(duì)象
public bool MoveNext()
{
_position++;
//返回一個(gè)布爾值,如果為真,則說(shuō)明枚舉沒(méi)有結(jié)束。
//如果為假,說(shuō)明已經(jīng)到集合的結(jié)尾,就結(jié)束此次枚舉
return (_position < People.Length);
}
public void Reset() => _position = -1;
}
為什么當(dāng)程序運(yùn)行到in時(shí),會(huì)呼叫方法MoveNext呢?我們并沒(méi)有直接調(diào)用這個(gè)方法啊?當(dāng)你試圖查詢(xún)IL時(shí),就會(huì)得到答案。實(shí)際上下面兩段代碼的作用是相同的:
foreach (T item in collection)
{
...
}
IEnumerator enumerator = collection.GetEnumerator();
while (enumerator.MoveNext())
{
T item = enumerator.Current;
...
}
注意:第二段代碼中,沒(méi)有呼叫Reset方法,也不需要呼叫。當(dāng)你呼叫時(shí),你會(huì)得到一個(gè)異常,這是因?yàn)榫幾g器沒(méi)有實(shí)現(xiàn)該方法。
新聞標(biāo)題:.NET教程:.NET面試題之IEnumerable
瀏覽地址:http://jinyejixie.com/article4/poseoe.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供企業(yè)建站、外貿(mào)網(wǎng)站建設(shè)、面包屑導(dǎo)航、品牌網(wǎng)站建設(shè)、建站公司、品牌網(wǎng)站制作
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)