zoukankan      html  css  js  c++  java
  • 浅谈C#数组(二)

    六.枚举集合
      在foreach语句中使用枚举,可以迭代集合中的元素,且无需知道集合中元素的个数。foreach语句使用一个枚举器。foreach会调用实现了IEnumerable接口的集合类中的GetEumerator()方法。GetEumerator()方法返回一个实现IEnumerator接口的对象枚举。foreach语句就可以使用IEnumerable接口迭代集合了。
      GetEumerator()方法在IEnumerable接口中定义。

      1.IEnumerator接口
      foreach语句使用IEnumerator接口的方法和属性,迭代集合中所有元素。IEnumerator接口定义了Current属性,来返回光标所在的元素,该接口的MoveNext()方法移动到集合的下一个元素上,如果有这个元素,该方法就返回true。如果集合不再有更多的元素,该方法就返回false.
      这个接口的泛型版本IEnumerator<T>派生自接口IDisposable,因此定义了Dispose()方法,来清理枚举器占用的资源。

      2.foreach语句
      C#中foreach语句不会解析为IL代码中的foreach语句。C#编译器会把foreach语句转换为IEnumerator接口的方法和属性。
      Person[] persons = {
        new Person { FirstName="Damon", LastName="Hill" },
        new Person { FirstName="Niki", LastName="Lauda" },
        new Person { FirstName="Ayrton", LastName="Senna" },
        new Person { FirstName="Graham", LastName="Hill" }
      };
      foreach (Person p in persons)
      {
        Console.WriteLine(p);
      }

      foreach语句会解析为下面的代码:
      IEnumerator<Person> enumerator = persons.GetEumerator();
      while(enumerator.MoveNext())
      {
        Person p = enumerator.Current;
        Console.WriteLine(p);
      }

      3.yield语句
      在C#2.0之前,foreach语句可以轻松的迭代集合,但创建枚举器需要做大量的工作。C#2.0添加了yield语句,以便创建枚举器。
      yield return 语句返回集合的一个元素,并移动到下一个元素。yield break可停止迭代。
      下面的例子实现返回两个字符串:
      public class HelloCollection
      {
        public IEnumerator<string> GetEnumerator()
        {
        yield return "Hello";
        yield return "World";
        }
      }
      客户端代码:
      var helloCollection = new HelloCollection();
      foreach (string s in helloCollection)
      {
        Console.WriteLine(s);
      }

      包含yield语句的方法或属性也称为迭代块。迭代块必须声明为返回IEnumerator或IEnumerable接口,或者这些接口的泛型版本。这个块可以包含多条yield return语句或yield break语句,但不能包含return语句。

      使用迭代块,编译器会生成一个yield类型,其中包含一个状态机,如下面代码所示:
      yield类型实现IEnumerator和IDisposable接口的方法和属性。下面的例子可以把yield类型看作内部类Enumerator。外部类的GetEnumerator()方法实例化并返回一个新的yield类型。在yield类型中,变量state定义了迭代的当前位置,每次调用MoveNext()时,当前位置都会改变。MoveNext()封装了迭代块的代码,并设置了current变量的值,从而使Current属性根据位置返回一个对象。
      public class HelloCollection
      {
        public IEnumerator<string> GetEnumerator()
        {
          return new Enumerator(0);
        }

      public class Enumerator:IEnumerator<string>,IEnumerator,IDisposable
      {
        private int state;
        private string current;

        public Enumerator(int state)
        {
          this.state = state;
        }

        bool System.Collections.IEnumerator.MoveNext()
        {
          switch(state)
          {
            case 0:
              current="hello";
              state =1;
              return true;
            case 1:
              current="world";
              state =2;
              return true;
            case 2:
              break;
          }

          return false;
        }

        void System.Collection>IEnumerator.Reset()
        {
          throw new NotSupportedException();
        }

        string System.Collections.Generic.IEnumerator<string>.Current
        {
          get
          {
            return current;
          }
        }

        object System.Collections.IEnumerator.Current
        {
          get
          {
            return current;
          }
        }

        void IDisposable.Dispose()
        {}
      }
    }


      yield语句会产生一个枚举器,而不仅仅生成一个包含的项的列表。这个枚举器通过foreach语句调用。从foreach中依次访问每一项,就会访问枚举器。这样就可以迭代大量的数据,而无需一次把所有的数据都读入内存。
        (1).迭代集合的不同方式
        可以使用yield return语句,以不同方式迭代集合。
        类MusicTitles可以用默认方式通过GetEnumerator()方法迭代标题,该方法不必在代码中编写,也可以用Reverse()逆序迭代标题,用Subset()方法迭代子集合:
        public class MusicTitles
        {
          string[] names = {
          "Tubular Bells", "Hergest Ridge",
          "Ommadawn", "Platinum" };

          public IEnumerator<string> GetEnumerator()
          {
            for (int i = 0; i < 4; i++)
            {
              yield return names[i];
            }
          }

          public IEnumerable<string> Reverse()
          {
            for (int i = 3; i >= 0; i--)
            {
              yield return names[i];
            }
          }

          public IEnumerable<string> Subset(int index, int length)
          {
            for (int i = index; i < index + length;i++)
            {
              yield return names[i];
            }
          }
        }
        客户端代码:
        var titles = new MusicTitles();
        foreach (var title in titles)
        {
          Console.WriteLine(title);
        }
        Console.WriteLine();

        Console.WriteLine("reverse");
        foreach (var title in titles.Reverse())
        {
          Console.WriteLine(title);
        }
        Console.WriteLine();

        Console.WriteLine("subset");
        foreach (var title in titles.Subset(2, 2))
        {
          Console.WriteLine(title);
        }


        (2).用yield return 返回枚举器

          

    public class GameMoves
      {
        private IEnumerator cross;
        private IEnumerator circle;
    
        public GameMoves()
        {
          cross = Cross();
          circle = Circle();
        }
    
        private int move = 0;
        const int MaxMoves = 9;
    
        public IEnumerator Cross()
        {
          while (true)
          {
            Console.WriteLine("Cross, move {0}", move);
            if (++move >= MaxMoves)
              yield break;
            yield return circle;
          }
        }
    
        public IEnumerator Circle()
        {
          while (true)
          {
            Console.WriteLine("Circle, move {0}", move);
            if (++move >= MaxMoves)
              yield break;
            yield return cross;
          }
        }
      }

        客户端代码:
        var game = new GameMoves();

        IEnumerator enumerator = game.Cross();
        while (enumerator.MoveNext())
        {
          enumerator = enumerator.Current as IEnumerator;
        }
        这样会交替调用Cross()和Circle()方法。

    七.元组(Tuple)
      元组可以合并不同类型的对象。元组起源于函数编程语言,如F#。在.NET Framework中,元组可用于所有的.Net语言。
      .NET Framework定义了8个泛型Tuple类和一个静态Tuple类,它们用作元组的工厂。不同的泛型Tuple类支持不同数量的元素。如,Tuple<T1>包含一个元素,Tuple<T1,T2>包含两个元素。
      Tuple<string, string> name = new Tuple<string, string>("Jochen", "Rindt");

      元组也可以用静态Tuple类的静态Create()方法创建。Create()方法的泛型参数定了要实例化的元组类型:
      public static Tuple<int, int> Divide(int dividend, int divisor)
      {
        int result = dividend / divisor;
        int reminder = dividend % divisor;

        return Tuple.Create<int, int>(result, reminder);
      }
      可以用属性Item1和Item2访问元组的项:
      var result = Divide(5, 2);
      Console.WriteLine("result of division: {0}, reminder: {1}", result.Item1, result.Item2);
      如果元组包含的项超过8个,就可以使用带8个参数的Tuple类定义。最后一个模板参数是TRest,表示必须给它传递一个元组。这样,就可以创建带任意个参数的元组了。
      var tuple = Tuple.Create<string, string, string, int, int, int, double, Tuple<int, int>>(
      "Stephanie", "Alina", "Nagel", 2009, 6, 2, 1.37, Tuple.Create<int, int>(52, 3490));

    八.结构比较
      数组和元组都实现接口IStructuralEquatable和IStructuralComparable。这两个接口不仅可以比较引用,还可以比较内容。这些接口都是显式实现的,所以在使用时需要把数组和元组强制转换为这个接口。
      IStructuralEquatable接口用于比较两个元组或数组是否有相同的内同,IStructuralComparable接口用于给元组或数组排序。

      IStructuralEquatable接口示例:
      编写实现IEquatable接口的Person类,IEquatable接口定义了一个强类型化的Equals()方法,比较FirstName和LastName的值:

        

    public class Person : IEquatable<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 override bool Equals(object obj)
        {
            if (obj == null)
                return base.Equals(obj);
            return Equals(obj as Person);
        }
    
        public override int GetHashCode()
        {
            return Id.GetHashCode();
        }
    
        #region IEquatable<Person> Members
    
        public bool Equals(Person other)
        {
          if (other == null)
            return base.Equals(other);
    
          return this.FirstName == other.FirstName && this.LastName == other.LastName;
        }
    
        #endregion
      }


      创建两个包含相同内容的Person类型的数组:
      var janet = new Person { FirstName = "Janet", LastName = "Jackson" };
      Person[] persons1 = { new Person { FirstName = "Michael", LastName = "Jackson" }, janet };
      Person[] persons2 = { new Person { FirstName = "Michael", LastName = "Jackson" }, janet };
      由于两个变量引用两个不同数组,所以!=返回True:
      if (persons1 != persons2)
        Console.WriteLine("not the same reference");

      对于IStructuralEquatable接口定义的Equals方法,第一个参数是object类型,第二个参数是IEqualityComparer类型。调用这个方法时,通过传递一个实现了EqualityComparer<T>的对象,就可以定义如何进行比较。通过EqualityComparer<T>类完成IEqualityComparer的一个默认实现。这个实现检查T类型是否实现了IEquatable接口,并调用IEquatable.Equals()方法。如果该类没有实现IEquatable接口,就调用Object基类中Equals()方法:
      if ((persons1 as IStructuralEquatable).Equals(persons2, EqualityComparer<Person>.Default))
      {
        Console.WriteLine("the same content");
      }


      元组示例:
      Tuple<>类提供了两个Epuals()方法:一个重写了Object基类中的Epuals方法,并把object作为参数,第二个由IStructuralEquatable接口定义,并把object和IEqualityComparer作为参数。

      var t1 = Tuple.Create<int, string>(1, "Stephanie");
      var t2 = Tuple.Create<int, string>(1, "Stephanie");
      if (t1 != t2)
      Console.WriteLine("not the same reference to the tuple");

      这个方法使用EqualityComparer<object>.Default获取一个ObjectEqualityComparer<object>,以进行比较。这样就会调用Object.Equals()方法比较元组的每一项:
      if (t1.Equals(t2))
        Console.WriteLine("equals returns true");

      还可以使用TupleComparer类创建一个自定义的IEqualityComparer
      TupleComparer tc = new TupleComparer();

      if ((t1 as IStructuralEquatable).Equals(t2, tc))
      {
        Console.WriteLine("yes, using TubpleComparer");
      }


      class TupleComparer : IEqualityComparer
      {
        #region IEqualityComparer Members

        public new bool Equals(object x, object y)
        {
          bool result = x.Equals(y);
          return result;
        }

        public int GetHashCode(object obj)
        {
          return obj.GetHashCode();
        }

        #endregion
      }

     

  • 相关阅读:
    memcached+狀態模式+工廠方法使用
    狀態模式
    UML类图
    Quartz.NET
    第四次作业---计算器的第二步
    做 fzu oj 1106 题目学到的
    做fzu oj 1045 做减法学到的sprintf()函数
    第三次补作业
    第三次作业随笔(new)包含了补作业
    远征系列---离港篇(学杂记)
  • 原文地址:https://www.cnblogs.com/afei-24/p/6738155.html
Copyright © 2011-2022 走看看