架构深渊

慢慢走进程序的深渊……关注领域驱动设计、测试驱动开发、设计模式、企业应用架构模式……积累技术细节,以设计架构为宗。
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

在Visual C# 2.0中创建优雅代码2

Posted on 2009-01-02 01:53  chen eric  阅读(242)  评论(0编辑  收藏  举报

 

第二个问题迭代器的实现也是难以解决的问题。虽然对于简单的应用实例中(如图3所示),实现是相当简单的,但是对于高级的数据结构,实现将非常复杂,例如二叉树,它需要递归遍历,并需在递归时维持迭代状态。另外,如果需要各种迭代选项,例如需要在一个链表中从头到尾和从尾到头选项,则此链表的代码就会因为使用多种迭代器实现而变得臃。这正是设计C# 2.0迭代器所要解决的问题。通过使用迭代器,可以让C#编译器生成IEnumerator的实现。C#编译器能够自动生成一个嵌套类来维持迭代状态。可以在泛型集合或特定于类型的集合中使用迭代器。开发人员需要做的只是告诉编译器在每个迭代中产生的是什么。如同手动提供迭代器一样,需要公开GetEnumerator方法,此方法是在实现IEnumerable接口或IEnumerable<ItemType>公开的。

  可以使用新的C#的yield return语句告诉编译器产生什么。例如,下面的代码显示了如何在city集合中使用C#迭代器来代替图2中的人工实现部分:

public class CityCollection : IEnumerable<string>
{
    
string[] m_Cities = {"New York","Paris","London"};

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

  此外,您还可以在非泛型集合中使用C#迭代器:

public class CityCollection : IEnumerable
{
    
string[] m_Cities = {"New York","Paris","London"};

    
public IEnumerator GetEnumerator()
    {
        
for(int i = 0; i<m_Cities.Length; i++)
            
yield return m_Cities[i];
    }
}

  此外,还可以在如图4所示的在完全泛型(Fully Generic)集合中使用C#迭代器。当使用泛型集合和迭代器时,从声明的集合(本例中的string)中,编译器就可以检索到foreach循环内IEnumerable
<ItemType>所用的特定类型:

LinkedList list 
= new LinkedList();

/* Some initialization of list, then */
foreach(string item in list)

    Trace.WriteLine(item);
}

图4在普通链表中使用迭代程序

//K is the key, T is the data item
class Node<K,T>
{
    
public K Key;
    
public T Item;
    
public Node<K,T> NextNode;
}

public class LinkedList<K,T> : IEnumerable<T>
{
    Node
<K,T> m_Head;

    
public IEnumerator<T> GetEnumerator()
    {
        Node
<K,T> current = m_Head;

        
while(current != null)
        {
            
yield return current.Item;
            current 
= current.NextNode;
        }
    } 

    
/* More methods and members */
}

  这与其他任何从泛型接口派生的相似。如果想中止迭代,请使用yield break语句。例如,下面的迭代器将仅仅产生数值1、2和3:

public IEnumerator GetEnumerator()
{
    
for(int i = 1;i< 5;i++)
    {
        
yield return i;

        
if(i > 2)
            
yield break;
    }
}

  这样,集合可以很容易地公开多个迭代器,每个迭代器都用于以不同的方式遍历集合。例如,要以倒序遍历CityCollection类,在这个类中提供了IEnumerable
<string>类型的Reverse属性,它是

public class CityCollection 

    
string[] m_Cities = {"New York","Paris","London"};

    
public IEnumerable Reverse
    {
        
get
        {
            
for(int i=m_Cities.Length-1; i>= 0; i--)
                
yield return m_Cities[i]; 
        }
    }
}

  这样就可以在foreach循环中使用Reverse属性:

CityCollection collection 
= new CityCollection();

foreach(string city in collection.Reverse)

    Trace.WriteLine(city);
}

  使用yield return语句是有一定限制的。包含yield return语句的方法或属性不能再包含其他return语句,否则会出现迭代中断并提示错误。不能在匿名方法中使用yield return语句,也不能将yield return语句放到带有catch块的try语句中(同样,也不能放在catch块或finally块中)。