服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - C#从foreach语句中枚举元素看数组详解

C#从foreach语句中枚举元素看数组详解

2022-01-05 13:00Simen.Net C#

这篇文章主要给大家介绍了关于C#从foreach语句中枚举元素看数组的相关资料,文中介绍的非常详细,对大家具有一定的参考学习价值,需要的朋友们下面来一起看看吧。

前言

在foreach语句中使用枚举,可以迭代数组或集合中的元素,且无须知道集合中的元素的个数。如图显示了调用foreach方法的客户端和集合之间的关系。数组或集合实现带GetEnumerator()方法的IEnumerable接口。GetEnumerator()方法返回一个实现lEnumerable接口的枚举,接着foreach语句就可以使用IEnumerable接口迭代集合了。

GetEnumerator()方法用IEnumerable接口定义,foreach语句并不真的需要在集合类中实现这个接口。有一个名为GetEnumerator()的方法它返回实现了IEnumerator接口的对象就足够了。

C#从foreach语句中枚举元素看数组详解

先定义一个Person类,这个类有自动实现的属性Firstname和Lastname,以及从Object类重写ToString方法和继承泛型接口IEquatable以比较两个对象是否相等,实现泛型接口IComparer以比较两个对象用来排序。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class Person : IEquatable<Person>,IComparable<Person>
 {
  public int Id { get; private set; }
  public string FirstName { get; set; }
  public string LastName { get; set; }
 
  public override string ToString()
  {
   return String.Format("{0}, {1} {2}", Id, FirstName, LastName);
  }
 
  public bool Equals(Person other)
  {
   if (other == null)
    return base.Equals(other);
 
   return this.FirstName == other.FirstName && this.LastName == other.LastName;
  }
 
  public int CompareTo(Person other)
  {
   if (other == null) throw new ArgumentNullException("other");
 
   int result = this.LastName.CompareTo(other.LastName);
   if (result == 0)
   {
    result = this.FirstName.CompareTo(other.FirstName);
   }
 
   return result;
  }
 
 }

创建一个三个元素的person数组,现对数组进行排序在用foreach循环访问数组中的元素并输出

?
1
2
3
4
5
6
7
8
Person[] persons = {
   new Person { FirstName = "Simen03", LastName = "Go" },
   new Person { FirstName = "Simen02", LastName = "Go" },
   new Person { FirstName = "Simen01", LastName = "Go" }
  };
  Array.Sort(persons);
  foreach (var person in persons)
   Console.WriteLine(person);

分析foreach (var person in persons)Console.WriteLine(person);这段代码IL代码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// loop start (head: IL_009b)
   IL_008a: ldloc.2
   IL_008b: ldloc.3
   IL_008c: ldelem.ref
   IL_008d: stloc.s person
   IL_008f: ldloc.s person
   IL_0091: call void [mscorlib]System.Console::WriteLine(object)
   IL_0096: nop
   IL_0097: ldloc.3
   IL_0098: ldc.i4.1
   IL_0099: add
   IL_009a: stloc.3
 
   IL_009b: ldloc.3
   IL_009c: ldloc.2
   IL_009d: ldlen
   IL_009e: conv.i4
   IL_009f: blt.s IL_008a
  // end loop

C#的foreach语句不会解析为IL代码中的foreach语句,C#编译器会把foreach语句转换为IEnumerable接口的方法和属性,foreach语句使用IEnumerator接口的方法和属性,迭代数组中的所有元素,为此,IEnumerator定义了Current属性,来返回光标所在的元素,该接口的MoveNext()方法移动到数组的下一个元素上,如果有这个元素该方法就返回true否则返回false,这个接口的泛型版本IEnumerator派生自接口IDisposable,因此定义了Dispose()方法来清理枚举器占用的资源,使用foreach语句会解析为下面的代码段

?
1
2
3
4
5
6
IEnumerator enumerator = persons.GetEnumerator();
  while (enumerator.MoveNext())
  {
   var person = enumerator.Current;
   Console.WriteLine(person);
  }

为了方便的创建枚举器,C#添加了yield语句,yield return 语句返回集合的一个元素,并移动到下一个元素,yield break 可停止迭代。使用迭代块,编译器会生成一个yield类型,其中包含一个状态机,如下代码段所示。yield 类型实现IEnumerator和IDisposable接口的属性和方法。在下面的例子中,可以把yield类型看作内部类Enumerator.外部类的GetEnumerator()方法实例化并返回一个新的yield类型。在yield类型中,变量state定义了迭代的当前位置,每次调用MoveNext()时,当前位置都会改变,MoveNext()封装了迭代代码,并设置了current变量的值,从而使Current属性根据位置返回一个对象。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
static void Main(string[] args)
 {
  var helloCollection = new HelloCollection();
  foreach (string s in helloCollection)
  {
   Console.WriteLine(s);
  }
 }
 
 public class HelloCollection
 {
  public IEnumerator<string> GetEnumerator()
  {
   yield return "Hello";
   yield return "World";
  }
 }
 public class HelloCollectionOther
 {
  public IEnumerator GetEnumertor()
  {
   return new Enumerator(0);
  }
  public class Enumerator : IEnumerator<string>, IEnumerator, IDisposable
  {
   private int state;
   private string current;
   public Enumerator(int state)
   {
    this.state = state;
   }
 
   public string Current => throw new NotImplementedException();
 
   object IEnumerator.Current
   {
    get { return current; }
   }
 
   public void Dispose()
   {
    throw new NotImplementedException();
   }
 
   public bool MoveNext()
   {
    switch (state)
    {
     case 0:current = "hello";
      state = 1;
      return true;
     case 1:current = "world";
      state = 2;
      return true;
     case 2:
      break;
    }
    return false;
   }
 
   public void Reset()
   {
    throw new NotImplementedException();
   }
  }
 }

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对服务器之家的支持。

原文链接:http://www.cnblogs.com/simen-tan/p/6816750.html

延伸 · 阅读

精彩推荐
  • C#C# 后台处理图片的几种方法

    C# 后台处理图片的几种方法

    本篇文章主要介绍了C# 后台处理图片的几种方法,非常具有实用价值,需要的朋友可以参考下。...

    IT小伙儿10162021-12-08
  • C#C#直线的最小二乘法线性回归运算实例

    C#直线的最小二乘法线性回归运算实例

    这篇文章主要介绍了C#直线的最小二乘法线性回归运算方法,实例分析了给定一组点,用最小二乘法进行线性回归运算的实现技巧,具有一定参考借鉴价值,需要...

    北风其凉8912021-10-18
  • C#C#基础之泛型

    C#基础之泛型

    泛型是 2.0 版 C# 语言和公共语言运行库 (CLR) 中的一个新功能。接下来通过本文给大家介绍c#基础之泛型,感兴趣的朋友一起学习吧...

    方小白7732021-12-03
  • C#c#学习之30分钟学会XAML

    c#学习之30分钟学会XAML

    一个界面程序的核心,无疑就是界面和后台代码,而xaml就是微软为构建应用程序界面而创建的一种描述性语言,也就是说,这东西是搞界面的...

    C#教程网8812021-12-10
  • C#聊一聊C#接口问题 新手速来围观

    聊一聊C#接口问题 新手速来围观

    聊一聊C#接口问题,新手速来围观,一个通俗易懂的例子帮助大家更好的理解C#接口问题,感兴趣的小伙伴们可以参考一下...

    zenkey7072021-12-03
  • C#浅谈C# winForm 窗体闪烁的问题

    浅谈C# winForm 窗体闪烁的问题

    下面小编就为大家带来一篇浅谈C# winForm 窗体闪烁的问题。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    C#教程网7962021-12-21
  • C#C#实现的文件操作封装类完整实例【删除,移动,复制,重命名】

    C#实现的文件操作封装类完整实例【删除,移动,复制,重命名】

    这篇文章主要介绍了C#实现的文件操作封装类,结合完整实例形式分析了C#封装文件的删除,移动,复制,重命名等操作相关实现技巧,需要的朋友可以参考下...

    Rising_Sun3892021-12-28
  • C#Unity3D UGUI实现缩放循环拖动卡牌展示效果

    Unity3D UGUI实现缩放循环拖动卡牌展示效果

    这篇文章主要为大家详细介绍了Unity3D UGUI实现缩放循环拖动展示卡牌效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参...

    诗远3662022-03-11