好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

浅谈C#数组(二)

可以先了解上一篇文章内容 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 和I StructuralComparable 。这两个接口不仅可以比较引用,还可以比较内容。这些接口都是显式实现的,所以在使用时需要把数组和元组强制转换为这个接口。    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   }

到此这篇关于浅谈C#数组的文章就介绍到这了,更多相关C#数组内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

查看更多关于浅谈C#数组(二)的详细内容...

  阅读:49次