List<>.Find() 使用Anonymous Delegate

在.NET 2.0之後開始支援了泛型,而且許多集合類別中也都支援了許多泛型函式。例如List<>中可以使用Find()函式來搜尋在List集合中的資料,將符合搜尋條件的資料給搜尋出來。

雖然List<>.Find()函式定義了要傳入的參數型別:Predicate,可是在實作上其實有許多種不同的方式。每種方式都有其優缺點,在此針對這些方式提供筆者個人的一些看法給大家參考。

首先先來看一段程式碼:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TestVS2008
{
    public class AnonyDelegate
    {
        public void Test()
        {
            List<string> listData = new List<string>();

            listData.Add("Eric");
            listData.Add("Anny");
            listData.Add("Sophia");

            string target = "Anny";

            // Search
            string found = listData.Find( IsMatch );
            Console.WriteLine("Search with Predicate function : " + found);
 
            // Search with SearchFunctor
            found = listData.Find(new SearchFunctor(target).SearchPredicate);
            Console.WriteLine("Search with SearchFunctor : " + found);

            // Search with anonymous delegate
            found = listData.Find(delegate(string str) { return target == str; });
            Console.WriteLine("Search with anonymous delegate : " + found);
        }

        protected bool IsMatch(string str)
        {
            return str == "Anny";
        }

        protected class SearchFunctor
        {
            protected string target = "";

            public SearchFunctor(string target)
            {
                this.target = target;
            }

            public bool SearchPredicate(string str)
            {
                return str == target;
            }
        }
    }
}

在上面這段程式碼中,筆者用了三種方式來查詢List<>集合中的資料,這三種方式分別是:

  1. 用一般的函式來作為Predicate所需的查詢函式
  2. 用一個查詢函式類別(SearchFunctor)來提供Predicate所需的查詢函式
  3. 用匿名委派函式來提供Predicate所需的查詢函式
在第一種方式中,可以看到我們想要查詢的目標字串,必須寫死在這個查詢函式中,因為這個查詢函式所能接收的參數個數只有一個,就是在被List.Find() 呼叫時所使用的比較對象。所以在使用上,外部的人無法再指定要查詢的目標。

為了解決無法指定查詢目標的問題,筆者第二種方式就改成提供一個查詢函式類別(SearchFunctor)來提供查詢的功能。這種方式的確是可以查詢出指定的目標,不過,感覺上是要多定義出一個類別來供其它人使用。

一開始是有想過使用匿名函式,只是想說匿名函式也是要符合List.Find()中的Predicate函式的定義,也就是只能接收List所傳入要進行比較的對象,無法指定要查詢的目標。所以才想到要定義一個查詢函式類別。不過,後來想到,其實匿名函式的程式碼也是存活在要使用的函式碼區段中,理應可以使用在這段程式碼中的變數

為了避免多定義一種類別,筆者就試著改用匿名函式來改寫。所以就像第三種方式那樣,在匿名函式的函式定義中,可以使用到外部可存取到的變數。

乍看之下使用匿名函式似乎比較有效率。不過試想一下,如果這段匿名函式中的Predicate函式的實作是比較複雜的話,那每次要使用的人,就必須再自行寫一段同樣的程式碼。那勢必會造成程式碼重複的問題,維護上會有其困難度。

所以,如果實作Predicate的函式內容是比較簡單的話,那建議就使用匿名函式。反之,如果實作的程式碼是比較複雜的話,那建議就用第二種方式,寫成一個類別,以便重複使用。


* 2010/4/1 補充
  有聽到有人說,當使用Find()時要搜尋某個目標值,而在給定的List<>等集合中有同時存在一個以上的目標值時,會出現Exception。這表示在搜尋時,.NET會整個掃過所有集合中的元素?如果這樣的話,真的太沒效率。所以我測試了一下,發現並不會出現任何Exception。它會傳回第一個找到的元素。

留言

這個網誌中的熱門文章

DOS Batch指令檔中如何記錄log資訊

用捷徑方式執行需帶入命令列參數的Windows Form程式

使用regular expression來match中括號(square bracket)