您的位置:首页 > 编程语言 > C#

C#发挥匿名委托的威力

2011-06-24 09:56 92 查看
这几天研究了一下Linq,C#
3.0中的“扩展方法”特性为IEnumerable<T>增加了诸如Where、Select等查询方法,这使得“语言集成查询”成为顺其
自然的事情。而C#3.0中Linq的实现也是建立在C#2.0的匿名委托的特性之上。

今天,我尝试在C#2.0中使用匿名委托模拟C#3.0中Where、Select等查询方法的实现。我将所有的查询方法作为静态方法在GenericHepler静态类中实现。

之前,我们先定义泛型委托:

public delegate TResult Func<T, TResult>(T source);

这个委托在后面的实现中需要用到。

作为基础,首先,我们需要实现ForSpecification方法,该方法的含义是:对集合中满足指定条件的元素执行指定方法调用。

/// <summary>

/// ForSpecification 对集合中满足predicate条件的元素执行action。如果没有条件,predicate传入null。

/// </summary>

public static void ForSpecification<TSource>(IEnumerable<TSource> collection, Action<TSource> action, Predicate<TSource> predicate)

{

if (predicate == null)

{

foreach (TSource obj in collection)

{

action(obj);

}

return;

}

foreach (TSource obj in collection)

{

if (predicate(obj))

{

action(obj);

}

}

}

有了ForSpecification的实现,我们就可以在其基础上实现ForEach和ForFirstSpecification:

#region ForEach

/// <summary>

/// ForEach  对集合中的每个元素执行action。

/// </summary>

public static void ForEach<TSource>(IEnumerable<TSource> collection, Action<TSource> action)

{

GenericHepler.ForSpecification<TSource>(collection, action, null);

}

#endregion

#region ForFirstSpecification

/// <summary>

/// ForSpecification 对集合中第一个满足predicate条件的元素执行action。如果没有条件,predicate传入null。

/// </summary>

public static void ForFirstSpecification<TSource>(IEnumerable<TSource> collection, Action<TSource> action, Predicate<TSource> predicate)

{

if (predicate == null)

{

foreach (TSource obj in collection)

{

action(obj);

break;

}

}

else

{

foreach (TSource obj in collection)

{

if (predicate(obj))

{

action(obj);

break;

}

}

}

}

#endregion

有了ForSpecification,我们就可以实现查询方法Where:

#region Where

/// <summary>

/// Where 从集合中选取符合条件的元素

/// </summary>

public static IList<TSource> Where<TSource>(IEnumerable<TSource> source, Predicate<TSource> predicate)

{

IList<TSource> list = new List<TSource>();

GenericHepler.ForSpecification(source, delegate(TSource ele) { list.Add(ele); } , predicate);

return list;

}

#endregion

对于C#3.0中的Select方法,其实现需要匿名类型的支持,而C#2.0中不支持匿名类型,所以,我用泛型来代替。我使用ConvertSpecification来模拟Select实现:

#region ConvertSpecification

/// <summary>

/// ConvertSpecification 将source中的符合predicate条件元素转换为TResult类型

/// </summary>

public static IList<TResult> ConvertSpecification<TSource, TResult>(IEnumerable<TSource> source, Func<TSource, TResult> converter, Predicate<TSource> predicate)

{

IList<TResult> list = new List<TResult>();

GenericHepler.ForSpecification<TSource>(source, delegate(TSource ele) { list.Add(converter(ele)); } ,predicate);

return list;

}

#endregion

converter委托用于从TSource类型对象构造TResult类型的对象。

有了ConvertSpecification实现,我们就可以在其上继续实现ConvertAll和ConvertFirstSpecification:

#region ConvertAll

/// <summary>

/// ConvertAll 将source中的每个元素转换为TResult类型

/// </summary>

public static IList<TResult> ConvertAll<TSource, TResult>(IEnumerable<TSource> source, Func<TSource, TResult> converter)

{

return GenericHepler.ConvertSpecification<TSource, TResult>(source, converter, null);

}

#endregion

#region ConvertFirstSpecification

/// <summary>

/// ConvertSpecification 将source中的符合predicate条件的第一个元素转换为TResult类型

/// </summary>

public static TResult ConvertFirstSpecification<TSource, TResult>(IEnumerable<TSource> source, Func<TSource, TResult> converter, Predicate<TSource> predicate)

{

TSource target = GenericHepler.GetFirstSpecification<TSource>(source, predicate);

if (target == null)

{

return default(TResult);

}

return converter(target);

}

#endregion

有了上面的基础,我们还可以实现ContainsSpecification方法:

#region ContainsSpecification

/// <summary>

/// ContainsSpecification 集合中是否包含满足predicate条件的元素。

/// </summary>

public static bool ContainsSpecification<TSource>(IEnumerable<TSource> source, Predicate<TSource> predicate, out TSource specification)

{

specification = default(TSource);

foreach (TSource element in source)

{

if (predicate(element))

{

specification = element;

return true;

}

}

return false;

}

#endregion

#region ContainsSpecification

/// <summary>

/// ContainsSpecification 集合中是否包含满足predicate条件的元素。

/// </summary>

public static bool ContainsSpecification<TSource>(IEnumerable<TSource> source, Predicate<TSource> predicate)

{

TSource specification;

return GenericHepler.ContainsSpecification<TSource>(source, predicate, out specification);

}

#endregion

代码中的注释已经将各个方法的用途说得非常清楚,下面我们举两个例子来看看如何使用它们以发挥它们的威力!

例子一:比如,我们要从当前玩家(IPlayer)列表中找出所有年龄大于30岁的玩家的ID,通常这样做:

public IList<string> GetOldPlayer()

{

IList<string> results = new List<string>();

foreach (IPlayer player in this.playerList)

{

if (player.Age > 30)

{

results.Add(player.ID);

}

}

return results;

}

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

public IList<string> GetOldPlayer()

{

return GenericHepler.ConvertSpecification<IPlayer, string>(this.playerList, delegate(IPlayer player) { return player.ID; } , delegate(IPlayer player) {return player.Age > 30 });

}

一句搞定。

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

通常,我们可以这样做:

public IList<IPlayer> GetPartners(params string[] excludedUserIDs)

{

IList<IPlayer> partnersList = new List<IPlayer>();

foreach (string userID in this.dicPlayers.Keys)

{

bool exclude = false;

foreach (string excludedUser in excludedUserIDs)

{

if (userID == excludedUser)

{

exclude = true;

break;

}

}

if (!exclude)

{

partnersList.Add(this.dicPlayers[userID]);

}

}

return partnersList;

}

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

public IList<IPlayer> GetPartners(params string[] excludedUserIDs)

{

return GenericHepler.Where<IPlayer>(this.dicPlayers.Values, delegate(IPlayer player) { return !GenericHepler.ContainsSpecification<string>(excludedUserIDs, delegate(string id) { return id == player.UserID; }); });

}

灵活地使用这些API,我们可以非常简洁地操作集合中的元素。

最后给出GenericHepler类的源码下载,其中还包含了几个未介绍的实用的API。files.cnblogs.com/zhuweisky/GenericHepler.rar
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: