• 浅析foreach原理


          在日常开发工作中,我们发现很多对象都能通过foreach来遍历,比如HashTable、Dictionary、数组等数据类型。那为何这些对象能通过foreach来遍历呢?如果写一个普通的Person类,也希望它能通过foreach来遍历应该怎么做?通过查看,发现HashTable、Dictionary、数组等数据类型都实现了一个叫IEnumerable(或其泛型版本)的接口。现在也来尝试下,让Person类实现这个接口(其实实不实现IEnumerable接口不是必须的,只要类型中有public IEnumerator GetEnumerator()这个方法即可):

     1 class Person:IEnumerable
     2     {
     3         public string[] _Name = new string[] { "sk", "jk", "yzk","wcw","ml" };
     4         public string Name { get; set; }
     5         public int Age { get; set; }
     6 
     7         public IEnumerator GetEnumerator()
     8         {
     9             return new PersonEnumerator(_Name);
    10         }
    11     }

    可以看到GetEnumerator()方法需要一个返回值类型为实现了IEnumerator的类型,那就写个类,让其实现IEnumerator接口:

     1 class PersonEnumerator:IEnumerator
     2     {
     3         public PersonEnumerator(string[] name)
     4         {
     5             this._names = name;
     6         }
     7 
     8         public string[] _names { get; set; }
     9         public int Index = -1;
    10 
    11         public object Current
    12         {
    13             get { return _names[Index]; }
    14         }
    15 
    16         public bool MoveNext()
    17         {
    18             Index++;
    19             if (Index>=_names.Length)
    20             {
    21                 return false;
    22             }
    23             else
    24             {
    25                 return true;
    26             }
    27         }
    28 
    29         public void Reset()
    30         {
    31             Index = -1;
    32         }
    33     }

    此时再运行程序,就发现Person类可以遍历了。运行结果如下:

    总结,一种类型要想通过foreach遍历,其内部必须有public IEnumerator GetEnumerator()这个方法,而通常的做法是让类型实现IEnumerable接口

    上面的代码还有一个问题,就是Person对象通过foreach遍历时,var并没有推断出是string类型而是object类型,这是因为Current就是object导致的,解决的方案就是泛型,看下面的代码:

     1 class Person
     2     {
     3         public string Name { get; set; }
     4         public int Age { get; set; }
     5         public string[] _Name = new string[] { "zxh", "jk", "ml", "wcw", "sk", "yzk","lmn" };
     6 
     7         public IEnumerator<string> GetEnumerator()
     8         {
     9             return new MyClass<string>(_Name);
    10         }
    11     }
    12 
    13     class MyClass<T>:IEnumerator<T>
    14     {
    15         public MyClass(T[] _Names)
    16         {
    17             this.names = _Names;
    18         }
    19         public T Current
    20         {
    21             get { return names[Index]; }
    22         }
    23 
    24         private T[] names { get; set; }
    25         private int Index = -1;
    26 
    27         public void Dispose()
    28         {
    29             //throw new NotImplementedException();
    30         }
    31 
    32         object IEnumerator.Current
    33         {
    34             get { throw new NotImplementedException(); }
    35         }
    36 
    37         public bool MoveNext()
    38         {
    39             Index++;
    40             if (Index>=names.Length)
    41             {
    42                 return false;
    43             }
    44             else
    45             {
    46                 return true;
    47             }
    48         }
    49 
    50         public void Reset()
    51         {
    52             Index = -1;
    53         }
    54     }

    使用的方法如下:

    1            Person p1 = new Person();
    2             foreach (var item in p1)
    3             {
    4                 Console.WriteLine(item);
    5             }

    可以看到var已经被推断成string.

    下面看一下,自己写的foreach遍历,还是那个Person类(必须有IEnumerator GetEnumerator()这个方法):

    把foreach遍历换成下面的代码:

    1            Person p1 = new Person();
    2             IEnumerator enumer = p1.GetEnumerator();
    3             while (enumer.MoveNext())
    4             {
    5                 Console.WriteLine(enumer.Current);
    6             }

    上面的代码也是能够正常执行的。

    扩展:

    1             List<string> strLst = new List<string>();   //查看定义得知,其实现了IEnumerable<T>和IEnumerable2个接口
    2             strLst.AddRange(new string[] { "sk", "jk", "yzk" });
    3             IEnumerator enumer = strLst.GetEnumerator();
    4             while (enumer.MoveNext())
    5             {
    6                 Console.WriteLine(enumer.Current);
    7             }

    一般没人会这么写,遍历一个对象还是直接写foreach。这只不过是其原理。

     1             Dictionary<int, char> dic = new Dictionary<int, char>();
     2             dic.Add(1, '');
     3             dic.Add(2, '');
     4             dic.Add(3, '');
     5             dic.Add(4, '');
     6             dic.Add(5, '');
     7             dic.Add(6, '');
     8             dic.Add(7,'');
     9             dic.Add(8,'');
    10             dic.Add(9,'');
    11 
    12             IEnumerator iEnumer = dic.GetEnumerator();
    13             while (iEnumer.MoveNext())
    14             {
    15                 KeyValuePair<int, char> item = (KeyValuePair<int, char>)iEnumer.Current;
    16                 Console.WriteLine(item.Key+"   "+item.Value);
    17             }
  • 相关阅读:
    如何做竞品分析报告
    软件架构入门
    系统内部矛盾的解决思路
    分表分库一
    Hbase随笔2
    Hbase随笔
    vertica在电信的应用
    理解RESTful架构
    百分点刘译璟
    数据产品的简介
  • 原文地址:https://www.cnblogs.com/chens2865/p/3860619.html
Copyright © 2020-2023  润新知