天天看點

【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))

        }

   有了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

        /// ForSpecification 對集合中第一個滿足predicate條件的元素執行action。如果沒有條件,predicate傳入null。

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

                    break;

            else

                    if (predicate(obj))

                    {

                        action(obj);

                        break;

                    }

   有了ForSpecification,我們就可以實作查詢方法Where:

       #region Where

        /// Where 從集合中選取符合條件的元素

        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;

        } 

   對于C#3.0中的Select方法,其實作需要匿名類型的支援,而C#2.0中不支援匿名類型,是以,我用泛型來代替。我使用ConvertSpecification來模拟Select實作:

       #region ConvertSpecification

        /// ConvertSpecification 将source中的符合predicate條件元素轉換為TResult類型

        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);

   converter委托用于從TSource類型對象構造TResult類型的對象。

   有了ConvertSpecification實作,我們就可以在其上繼續實作ConvertAll和ConvertFirstSpecification:

       #region ConvertAll

        /// ConvertAll 将source中的每個元素轉換為TResult類型

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

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

        #region ConvertFirstSpecification

        /// ConvertSpecification 将source中的符合predicate條件的第一個元素轉換為TResult類型

        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

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

        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

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

            TSource specification;

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

   代碼中的注釋已經将各個方法的用途說得非常清楚,下面我們舉兩個例子來看看如何使用它們以發揮它們的威力!

   例子一:比如,我們要從目前玩家(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;

                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,我們可以非常簡潔地操作集合中的元素。

繼續閱讀