zoukankan      html  css  js  c++  java
  • 【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静态类中实现。
       之前,我们先定义泛型委托:

       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。


  • 相关阅读:
    NodeJs学习历程
    MongoDb学习历程
    递归函数为什么要防止栈溢出
    *args 是可变参数,args 接收的是一个 tuple; **kw 是关键字参数,kw 接收的是一个 dict。
    list和tuple的区别
    python源码阅读
    常用的线程池有哪些?
    备份
    假设你正在爬楼梯,需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶,你有多少种不同的方法可以爬到楼顶呢?
    最后一个单词的长度
  • 原文地址:https://www.cnblogs.com/zhuweisky/p/843127.html
Copyright © 2011-2022 走看看