在VB和C#中,要枚举一个列表中的若干的对象,很容易使用 foreach 结构,抛开性能的话题,foreach 结构简单直观,使用方便。深受广大VB和C#程序员的喜爱。
在C#中,若要实现一个可用于foreach结构的枚举器必须实现 System.Collections.IEnumerable ,然后使用 "foreach( 成员类型 obj in 枚举器变量 )" 的语法结构来使用该枚举器。
但有些时候,枚举器有时枚举器返回的对象类型可能是变化的,为此我们在foreach循环中需要进行类型转换和判断。
比如我们对以下XML文档的根节点的子XML元素进行枚举
文本内容
<!-- 注释1 -->
<aa>aaaaaaaaa</aa>
<!-- 注释2 -->
<bb>bbbbbbbbbbb</bb>
文本内容
<!-- 注释3 -->
<cc>cccccccc</cc>
<!-- 注释4 -->
<!-- 注释5 -->
<!-- 注释6 -->
<dd>eeeeeeeeeee</dd>
<!-- 注释7 -->
<devdoc>ffffffffffffff</devdoc>
<devdoc>aa</devdoc>
<devdoc>文本内容</devdoc>
</members>
很显然,这个XML文档的根节点下有一些不是XML元素的成员。比如文本和注释。此时遍历时需要判断当前节点是否是XML元素类型。为此被迫多些上几行代码,类似的代码写多了那就烦了。
为了避免这种麻烦,在此提出支持类型过滤的枚举器的概念,这种枚举器是其他枚举器的包装,它能从其他枚举器获得对象,并返回指定类型的当前对象,这样我们使用支持类型过滤的枚举器时,只需要在开始遍历的时候指明所需的对象类型,进行循环遍历的时候就不必再进行类型判断,
使用类型过滤的枚举器,能让我们编写代码时避免一些类型判断的处理,简单可靠,写代码的时候比较舒服。
此时有人会提出性能问题。在此根据我个人的编程经验,大部分情况下,性能基本大部分是决定于算法,算法设计得好,性能就不会差。各种技术上的包装可能降低性能,但属于细节,无伤大雅。但在少数情况下,算法的具体实现过程就能很大的影响性能,比如若foreach需要完成很大规模的遍历工作量,则此时是否使用这种支持类型过滤的遍历器则需要认真判断了。
以下是演示代码
namespace EnumFilter
{
/// <summary>
/// Class1 的摘要说明。
/// </summary>
class Class1
{
/// <summary>
/// 带类型过滤的枚举器对象的测试代码
/// </summary>
/// <remarks>编制 袁永福( http://www.xdesigner.cn ) 2007-3-20</remarks>
[STAThread]
static void Main(string[] args)
{
System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
doc.LoadXml(@"
<members>
文本内容
<!-- 注释1 -->
<aa>aaaaaaaaa</aa>
<!-- 注释2 -->
<bb>bbbbbbbbbbb</bb>
文本内容
<!-- 注释3 -->
<cc>cccccccc</cc>
<!-- 注释4 -->
<!-- 注释5 -->
<!-- 注释6 -->
<dd>eeeeeeeeeee</dd>
<!-- 注释7 -->
<devdoc>ffffffffffffff</devdoc>
<devdoc>aa</devdoc>
<devdoc>文本内容</devdoc>
</members>");
// 未使用类型过滤的枚举器
// 需要在循环中进行类型的转换和判断
foreach( System.Xml.XmlNode node in doc.DocumentElement.ChildNodes )
{
// 若不加上下行遍历则会发生转换无效错误
System.Xml.XmlElement element = node as System.Xml.XmlElement ;
if( element != null )
{
System.Console.WriteLine( element.Name + " # " + element.InnerText );
}
}
System.Console.WriteLine("######################");
// 使用类型过滤的枚举器
// 不需要在循环中进行类型的转换和判断
foreach( System.Xml.XmlElement element in
new TypeFilterEnum( doc.DocumentElement.ChildNodes , typeof( System.Xml.XmlElement )))
{
System.Console.WriteLine( element.Name + " # " + element.InnerText );
}
System.Console.WriteLine("按回车键退出");
System.Console.ReadLine();
}
/// <summary>
/// 带类型过滤的枚举器对象
/// </summary>
/// <remarks>编制 袁永福( http://www.xdesigner.cn ) 2007-3-20</remarks>
public class TypeFilterEnum : System.Collections.IEnumerable
{
/// <summary>
/// 初始化对象
/// </summary>
/// <param name="e">枚举器</param>
/// <param name="t">符合的类型</param>
public TypeFilterEnum( System.Collections.IEnumerator e , System.Type t )
{
this.myEnum = e ;
this.myMatchType = t ;
}
/// <summary>
/// 初始化对象
/// </summary>
/// <param name="e">枚举器</param>
/// <param name="t">符合的类型</param>
public TypeFilterEnum( System.Collections.IEnumerable e , System.Type t )
{
this.myEnum = e.GetEnumerator();
this.myMatchType = t ;
}
/// <summary>
/// 返回枚举器对象
/// </summary>
/// <returns>枚举器对象</returns>
public System.Collections.IEnumerator GetEnumerator()
{
MyEnumerator e = new MyEnumerator();
e.myEnum = myEnum ;
e.myMatchType = myMatchType ;
return e ;
}
#region 内部代码 **************************************************
private System.Collections.IEnumerator myEnum = null;
private System.Type myMatchType = null;
private class MyEnumerator : System.Collections.IEnumerator
{
internal System.Collections.IEnumerator myEnum = null;
internal System.Type myMatchType = null;
public void Reset()
{
myEnum.Reset();
}
public object Current
{
get
{
return myEnum.Current ;
}
}
public bool MoveNext()
{
while( myEnum.MoveNext())
{
object o = myEnum.Current ;
System.Type t = o.GetType();
if( t.Equals( myMatchType ) || t.IsSubclassOf( t ))
return true ;
}
return false;
}
}
#endregion
}//public class TypeFilterEnum : System.Collections.IEnumerable
}
}
袁永福( http://www.xdesigner.cn ) 2007-3-20