zhuweisky

君子之行,静以修身,俭以养德。非淡泊无以明志,非宁静无以致远。

ESFramework,基于.NET的通信框架。DataRabbit,轻量的数据访问框架。sky.zhuwei@163.com
posts - 185, comments - 1261, trackbacks - 87, articles - 1
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

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

Posted on 2007-08-04 20:14 zhuweisky 阅读(3685) 评论(10)  编辑 收藏 所属分类: C#专栏
   这几天研究了一下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。


Feedback

#1楼    回复  引用  查看    

2007-08-04 21:04 by Adrian.h      
如果用上C# 3.0的语法, 这句代码
return GenericHepler.Where<IPlayer>(this.dicPlayers.Values, delegate(IPlayer player) { return !GenericHepler.ContainsSpecification<string>(excludedUserIDs, delegate(string id) { return id == player.UserID; }); });
会看上去简约许多~~

使用复杂一点的匿名委托来实现闭包可能会被编译成一个类,如果大量的循环使用它可能会因为在运行时生成大量对象增加运行开销。。。

#2楼    回复  引用    

2007-08-04 21:22 by 非常酷 [未注册用户]
C# 3.0目前还没有接触过。
今天倒是尝试用vs2008做了一个Web。还是基于(2.0)的。

#3楼    回复  引用    

2007-08-04 22:47 by henry [未注册用户]
自己框架的AOP也是建立在匿名委托之上,匿名委托的确是一个很用的东西.
不过嵌套太多代码不直观.VS2005也不能解决嵌套委托的语法布局问题.在维护的时候真的很麻烦...

#4楼    回复  引用  查看    

2007-08-04 23:22 by idior      
http://www.cnblogs.com/idior/archive/2005/04/23/143814.html

两年前的东东 -_-||

#5楼    回复  引用  查看    

2007-08-06 08:46 by 大石头      
强大。
真感到自己认识的浅薄

#6楼    回复  引用  查看    

2007-08-06 10:21 by 装配脑袋      
为啥不用yield return呢?

#7楼    回复  引用  查看    

2007-08-06 10:46 by 紫色阴影      
@装配脑袋
yield return好像不能用在匿名方法里面吧

#8楼    回复  引用    

2007-11-12 15:55 by minidxer [未注册用户]
收藏~

#9楼    回复  引用  查看    

2007-12-27 22:48 by txdlf      
--引用--------------------------------------------------
idior: http://www.cnblogs.com/idior/archive/2005/04/23/143814.html

两年前的东东 -_-||
--------------------------------------------------------
当时要是能完善下去那太强了

标题  
姓名  
主页
Email (只有博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      


相关链接: