.NET教程:.NET面试题之IEnumerable

.NET教程,今天给大家介绍的是:.NET 面试题之IEnumerable ,这是在面试的时候可能会碰到的一道题目,这道题的注解分为了两个部分,这一篇是第一部分!

成都创新互联公司网站建设由有经验的网站设计师、开发人员和项目经理组成的专业建站团队,负责网站视觉设计、用户体验优化、交互设计和前端开发等方面的工作,以确保网站外观精美、成都做网站、网站制作易于使用并且具有良好的响应性。

什么是IEnumerable?

IEnumerable及IEnumerable的泛型版本IEnumerable是一个接口,它只含有一个方法GetEnumerator。Enumerable这个静态类型含有很多扩展方法,其扩展的目标是IEnumerable。

实现了这个接口的类可以使用Foreach关键字进行迭代(迭代的意思是对于一个集合,可以逐一取出元素并遍历之)。

实现这个接口必须实现方法GetEnumerator。

如何实现一个继承IEnumerable的类型?

实现一个继承IEnumerable的类型等同于实现方法GetEnumerator。想知道如何实现方法GetEnumerator,不妨思考下实现了GetEnumerator之后的类型在Foreach之下的行为:

可以获得第一个或当前成员

可以移动到下一个成员

可以在集合没有下一个成员时退出循环。

假设我们有一个很简单的Person类(例子来自MSDN):

public class Person

{

public Person(string fName, string lName)

{

FirstName = fName;

LastName = lName;

}

public string FirstName;

public string LastName;

}

然后我们想构造一个没有实现IEnumerable的类型,其储存多个Person,然后再对这个类型实现IEnumerable。

这个类型实际上的作用就相当于Person[]或List,但我们不能使用它们,因为它们已经实现了IEnumerable,故我们构造一个People类,模拟很多人(People是Person的复数形式)。

这个类型允许我们传入一组Person的数组。所以它应当有一个Person[]类型的成员,和一个构造函数,其可以接受一个Person[],然后将Person[]类型的成员填充进去作为初始化。

//People类就是Person类的集合

//但我们不能用List或者Person[],因为他们都实现了IEnumerable

//我们要自己实现一个IEnumerable

//所以请将People类想象成List或者类似物

public class People : IEnumerable

{

private readonly Person[] _people;

public People(Person[] pArray)

{

//构造一个Person的集合

_people = new Person[pArray.Length];

for (var i = 0; i < pArray.Length; i++)

{

_people[i] = pArray[i];

}

}

//实现IEnumerable需要实现GetEnumerator方法

public IEnumerator GetEnumerator()

{

throw new NotImplementedException();

}

}

我们的主函数应当是:

public static void Main(string[] args)

{

//新的Person数组

Person[] peopleArray =

{

new Person("John", "Smith"),

new Person("Jim", "Johnson"),

new Person("Sue", "Rabon"),

};

//People类实现了IEnumerable

var peopleList = new People(peopleArray);

//枚举时先访问MoveNext方法

//如果返回真,则获得当前对象,返回假,就退出此次枚举

foreach (Person p in peopleList)

Console.WriteLine(p.FirstName + " " + p.LastName);

}

但现在我们的程序不能运行,因为我们还没实现GetEnumerator方法。

实现方法GetEnumerator

GetEnumerator方法需要一个IEnumerator类型的返回值,这个类型是一个接口,所以我们不能这样写:

return new IEnumerator();

因为我们不能实例化一个接口。我们必须再写一个类PeopleEnumerator,它继承这个接口,实现这个接口所有的成员:Current属性,两个方法MoveNext和Reset。于是我们的代码又变成了这样:

//实现IEnumerable需要实现GetEnumerator方法

public IEnumerator GetEnumerator()

{

return new PeopleEnumerator();

}

在类型中:

public class PeopleEnumerator : IEnumerator

{

public bool MoveNext()

{

throw new NotImplementedException();

}

public void Reset()

{

throw new NotImplementedException();

}

public object Current { get; }

}

现在问题转移为实现两个方法,它们的功能看上去一目了然:一个负责将集合中Current向后移动一位,一个则将Current初始化为0。

我们可以查看IEnumerator元数据,其解释十分清楚:

Enumerator代表一个类似箭头的东西,它指向这个集合当前迭代指向的成员

IEnumerator接口类型对非泛型集合实现迭代

Current表示集合当前的元素,我们需要用它仅有的get方法取得当前元素

MoveNext方法根据Enumerator是否可以继续向后移动返回真或假

Reset方法将Enumerator移到集合的开头

通过上面的文字,我们可以理解GetEnumerator方法,就是获得当前Enumerator指向的成员。我们引入一个整型变量position来记录当前的位置,并且先试着写下:

public class PeopleEnumerator : IEnumerator

{

public Person[] _peoples;

public object Current { get; }

//当前位置

public int position;

//构造函数接受外部一个集合并初始化自己内部的属性_peoples

public PeopleEnumerator(Person[] peoples)

{

_peoples = peoples;

}

//如果没到集合的尾部就移动position,返回一个bool

public bool MoveNext()

{

if (position < _peoples.Length)

{

position++;

return true;

}

return false;

}

public void Reset()

{

position = 0;

}

}

这看上去好像没问题,但一执行之后却发现:

当执行到MoveNext方法时,position会先增加1,这导致第一个元素(在位置0)会被遗漏,故position的初始值应当为-1而不是0

当前位置变量position显然应该是私有的

需要编写Current属性的get方法取出当前位置(position)上的集合成员

通过不断的调试,最后完整的实现应当是:

public class PeopleEnumerator : IEnumerator

{

public Person[] People;

//每次运行到MoveNext或Reset时,利用get方法自动更新当前位置指向的对象

object IEnumerator.Current

{

get

{

try

{

//当前位置的对象

return People[_position];

}

catch (IndexOutOfRangeException)

{

throw new InvalidOperationException();

}

}

}

//当前位置

private int _position = -1;

public PeopleEnumerator(Person[] people)

{

People = people;

}

//当程序运行到foreach循环中的in时,就调用这个方法获得下一个person对象

public bool MoveNext()

{

_position++;

//返回一个布尔值,如果为真,则说明枚举没有结束。

//如果为假,说明已经到集合的结尾,就结束此次枚举

return (_position < People.Length);

}

public void Reset() => _position = -1;

}

为什么当程序运行到in时,会呼叫方法MoveNext呢?我们并没有直接调用这个方法啊?当你试图查询IL时,就会得到答案。实际上下面两段代码的作用是相同的:

foreach (T item in collection)

{

...

}

IEnumerator enumerator = collection.GetEnumerator();

while (enumerator.MoveNext())

{

T item = enumerator.Current;

...

}

注意:第二段代码中,没有呼叫Reset方法,也不需要呼叫。当你呼叫时,你会得到一个异常,这是因为编译器没有实现该方法。

新闻标题:.NET教程:.NET面试题之IEnumerable
浏览地址:https://www.cdcxhl.com/article4/poseoe.html

成都网站建设公司_创新互联,为您提供企业建站外贸网站建设面包屑导航品牌网站建设建站公司品牌网站制作

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联

成都定制网站建设