`
soboer
  • 浏览: 1314070 次
文章分类
社区版块
存档分类
最新评论

【C#2.0】发挥匿名委托的威力!

 
阅读更多
这几天研究了一下Linq,C# 3.0中的“扩展方法”特性为IEnumerable<T>增加了诸如Where、Select等查询方法,这使得“语言集成查询”成为顺其自然的事情。而C#3.0中Linq的实现也是建立在C#2.0的匿名委托的特性之上。
今天,我尝试在C#2.0中使用匿名委托模拟C#3.0中Where、Select等查询方法的实现。我将所有的查询方法作为静态方法在GenericHepler静态类中实现。
之前,我们先定义泛型委托:
publicdelegateTResultFunc<T,TResult>(Tsource);
这个委托在后面的实现中需要用到。

作为基础,首先,我们需要实现ForSpecification方法,该方法的含义是:对集合中满足指定条件的元素执行指定方法调用。
///<summary>
///ForSpecification对集合中满足predicate条件的元素执行action。如果没有条件,predicate传入null。
///</summary>
publicstaticvoidForSpecification<TSource>(IEnumerable<TSource>collection,Action<TSource>action,Predicate<TSource>predicate)
{
if(predicate==null)
{
foreach(TSourceobjincollection)
{
action(obj);
}

return;
}

foreach(TSourceobjincollection)
{
if(predicate(obj))
{
action(obj);
}
}
}

有了ForSpecification的实现,我们就可以在其基础上实现ForEach和ForFirstSpecification:
#regionForEach
///<summary>
///ForEach对集合中的每个元素执行action。
///</summary>
publicstaticvoidForEach<TSource>(IEnumerable<TSource>collection,Action<TSource>action)
{
GenericHepler.ForSpecification
<TSource>(collection,action,null);
}
#endregion

#regionForFirstSpecification
///<summary>
///ForSpecification对集合中第一个满足predicate条件的元素执行action。如果没有条件,predicate传入null。
///</summary>
publicstaticvoidForFirstSpecification<TSource>(IEnumerable<TSource>collection,Action<TSource>action,Predicate<TSource>predicate)
{
if(predicate==null)
{
foreach(TSourceobjincollection)
{
action(obj);
break;
}
}
else
{
foreach(TSourceobjincollection)
{
if(predicate(obj))
{
action(obj);
break;
}
}
}
}
#endregion

有了ForSpecification,我们就可以实现查询方法Where:
#regionWhere
///<summary>
///Where从集合中选取符合条件的元素
///</summary>
publicstaticIList<TSource>Where<TSource>(IEnumerable<TSource>source,Predicate<TSource>predicate)
{
IList
<TSource>list=newList<TSource>();
GenericHepler.ForSpecification(source,
delegate(TSourceele){list.Add(ele);},predicate);
returnlist;
}
#endregion
对于C#3.0中的Select方法,其实现需要匿名类型的支持,而C#2.0中不支持匿名类型,所以,我用泛型来代替。我使用ConvertSpecification来模拟Select实现:
#regionConvertSpecification
///<summary>
///ConvertSpecification将source中的符合predicate条件元素转换为TResult类型
///</summary>
publicstaticIList<TResult>ConvertSpecification<TSource,TResult>(IEnumerable<TSource>source,Func<TSource,TResult>converter,Predicate<TSource>predicate)
{
IList
<TResult>list=newList<TResult>();
GenericHepler.ForSpecification
<TSource>(source,delegate(TSourceele){list.Add(converter(ele));},predicate);
returnlist;
}
#endregion
converter委托用于从TSource类型对象构造TResult类型的对象。
有了ConvertSpecification实现,我们就可以在其上继续实现ConvertAll和ConvertFirstSpecification:
#regionConvertAll
///<summary>
///ConvertAll将source中的每个元素转换为TResult类型
///</summary>
publicstaticIList<TResult>ConvertAll<TSource,TResult>(IEnumerable<TSource>source,Func<TSource,TResult>converter)
{
returnGenericHepler.ConvertSpecification<TSource,TResult>(source,converter,null);
}
#endregion

#regionConvertFirstSpecification
///<summary>
///ConvertSpecification将source中的符合predicate条件的第一个元素转换为TResult类型
///</summary>
publicstaticTResultConvertFirstSpecification<TSource,TResult>(IEnumerable<TSource>source,Func<TSource,TResult>converter,Predicate<TSource>predicate)
{
TSourcetarget
=GenericHepler.GetFirstSpecification<TSource>(source,predicate);

if(target==null)
{
returndefault(TResult);
}

returnconverter(target);
}
#endregion
有了上面的基础,我们还可以实现ContainsSpecification方法:
#regionContainsSpecification
///<summary>
///ContainsSpecification集合中是否包含满足predicate条件的元素。
///</summary>
publicstaticboolContainsSpecification<TSource>(IEnumerable<TSource>source,Predicate<TSource>predicate,outTSourcespecification)
{
specification
=default(TSource);
foreach(TSourceelementinsource)
{
if(predicate(element))
{
specification
=element;
returntrue;
}
}

returnfalse;
}
#endregion

#regionContainsSpecification
///<summary>
///ContainsSpecification集合中是否包含满足predicate条件的元素。
///</summary>
publicstaticboolContainsSpecification<TSource>(IEnumerable<TSource>source,Predicate<TSource>predicate)
{
TSourcespecification;
returnGenericHepler.ContainsSpecification<TSource>(source,predicate,outspecification);
}
#endregion


代码中的注释已经将各个方法的用途说得非常清楚,下面我们举两个例子来看看如何使用它们以发挥它们的威力!
例子一:比如,我们要从当前玩家(IPlayer)列表中找出所有年龄大于30岁的玩家的ID,通常这样做:

publicIList<string>GetOldPlayer()
{
IList
<string>results=newList<string>();
foreach(IPlayerplayerinthis.playerList)
{
if(player.Age>30)
{
results.Add(player.ID);
}
}

returnresults;
}

如果使用上面我们封装的API,则可以非常简单地达到目的:

publicIList<string>GetOldPlayer()
{
returnGenericHepler.ConvertSpecification<IPlayer,string>(this.playerList,delegate(IPlayerplayer){returnplayer.ID;},delegate(IPlayerplayer){returnplayer.Age>30});
}

一句搞定。

例子二:我们要从当前的玩家字典(Dictionary)中取出所有ID不是指定集合中的ID的其它玩家列表。
通常,我们可以这样做:

publicIList<IPlayer>GetPartners(paramsstring[]excludedUserIDs)
{
IList
<IPlayer>partnersList=newList<IPlayer>();
foreach(stringuserIDinthis.dicPlayers.Keys)
{
boolexclude=false;
foreach(stringexcludedUserinexcludedUserIDs)
{
if(userID==excludedUser)
{
exclude
=true;
break;
}
}

if(!exclude)
{
partnersList.Add(
this.dicPlayers[userID]);
}
}
returnpartnersList;
}

使用上面我们封装的API,则非常简单:

publicIList<IPlayer>GetPartners(paramsstring[]excludedUserIDs)
{
returnGenericHepler.Where<IPlayer>(this.dicPlayers.Values,delegate(IPlayerplayer){return!GenericHepler.ContainsSpecification<string>(excludedUserIDs,delegate(stringid){returnid==player.UserID;});});
}

灵活地使用这些API,我们可以非常简洁地操作集合中的元素。
最后给出GenericHepler类的源码下载,其中还包含了几个未介绍的实用的API。


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics