程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> .Net迭代器新思路

.Net迭代器新思路

編輯:關於.NET

在通常的情況下,我們非常習慣於用foreach來迭代容器中的元素。雖然比起for循環來說,foreach可以讓我們少打一些字母。

如果我們有下面的需求:把一個整數的容器中所有元素乘2,然後保存在新的容器中,那麼我們通常會寫下下面的代碼:

1int[] datas = { 1, 3, 2, 5, 7, 94, 1, 8, 42, 74, 8 };
2ArrayList src = new ArrayList(datas);
3ArrayList dest = new ArrayList();
4foreach (object obj in src)
5{
6  dest.Add(((int)obj) * 2);
7}
8

對於表達來說,我們真正需要注意的是“dest.Add(((int)obj) * 2);”這行代碼。而在這行代碼中,最重要的是“((int)obj) * 2”代碼。而剩下的代碼僅僅是為了輔助完成上面的任務。

真正在開發中,我們可能會經常遇到上面的命題。如果每次都來寫foreach,然後是幾行的輔助代碼,確實感覺有點繁瑣。那麼我們有什麼好的方法嗎?能把foreach封裝起來就好了。

在.Net的System.Collections.Generic命名空間裡,我們會發現List類有下面幾個方法:

ConvertAll 將當前 List 中的元素轉換為另一種類型,並返回包含轉換後的元素的列表。

Find 搜索與指定謂詞所定義的條件相匹配的元素,並返回整個 List 中的第一個匹配元素。

FindAll 檢索與指定謂詞所定義的條件相匹配的所有元素。

FindLast 搜索與指定謂詞所定義的條件相匹配的元素,並返回整個 List 中的最後一個匹配元素。

FindLastIndex 已重載。 搜索與指定謂詞所定義的條件相匹配的元素,返回 List 或它的一部分中最後一個匹配項的從零開始的索引。

ForEach 對 List 的每個元素執行指定操作。

這些方法都有一個特殊的參數:代理。這些代理都是在System命名空間中聲明的。

下面我們還是沿用剛才的命題,用ConvertAll來寫一段代碼完成任務。

1int[] datas = { 1, 3, 2, 5, 7, 94, 1, 8, 42, 74, 8 };
2List<int> src = new List<int>(datas);
3List<int> dest = src.ConvertAll(delegate(int val)
4{
5  return val * 2;
6});

現在,你看不到foreach循環,而且也不用負責向dest中加入元素,你唯一需要關注的就是“val * 2”。現在都是.Net 3.5了,我們應該換上時髦的Lambda表達式!

1int[] datas = { 1, 3, 2, 5, 7, 94, 1, 8, 42, 74, 8 };
2List<int> src = new List<int>(datas);
3List<int> dest = src.ConvertAll(val => val * 2);

現在讓我們來數數,原本聲明dest,foreach外加val * 2的3行代碼,現在一行就完成了。而且從代碼上你一眼就可以看明白“List<int> dest = src.ConvertAll(val => val * 2);”這句話是什麼意思:聲明一個dest容器,它應該是指向一個src以“val => val * 2”為規則轉化而成的容器。

那麼現在你肯定想知道這是怎麼做到的。其實並不難,讓我們來以ArrayList為基類創建一個自己的List類:MyList。

1class MyList<T> : ArrayList
2{
3  public MyList() { }
4  public MyList(ICollection list) : base(list) { }
5  public MyList(int capacity) : base(capacity) { }
6}

好了,在此先創建一個泛型的MyList。繼承自ArrayList。接下來,我們可以聲明3個代理,負責3種不同的操作:ForEach、FindAll、CollectAll

1public delegate void ForEachAction<T>(T val);
2public delegate R CollectAllFunc<T, R>(T val);
3public delegate bool FindAllFunc<T>(T val);

下面我們就來創建ForEach方法、FindAll方法和CollectAll方法。首先,我們來看看ForEach。這個最簡單。

1public MyList<T> ForEach(ForEachAction<T> act)
2{
3  foreach (object obj in this)
4  {
5    act((T)obj);
6  }
7  return this;
8}

這類方法的設計思路大體是這樣的。把變動的代碼視為一個代碼塊,有輸入和輸出。這樣就可以把變動的代碼抽象成一個函數。於是我們就可以把變動的代碼分析一下,得出需要接受多少參數,然後會返回什麼結果。最後設計一個代理來限定這個代碼塊的大模樣。而ForEach方法中,僅僅是固定不變的代碼,如foreach循環。在最後之所以返回this,目的是為了鏈式調用:

list.ForEach(…).Add(…)

至於CollectAll和FindAll方法也使用同樣的設計思路:

1public MyList<RType> CollectAll<RType>(CollectAllFunc<T, RType> act)
2{
3  MyList<RType> ret = new MyList<RType>();
4  foreach (object obj in this)
5  {
6    ret.Add(act((T)obj));
7  }
8  return ret;
9}
10
11public MyList<T> FindAll(FindAllFunc<T> act)
12{
13  MyList<T> ret = new MyList<T>();
14  foreach (object obj in this)
15  {
16    if(act((T)obj)) ret.Add(obj);
17  }
18  return ret;
19}

下面我們來看看這個MyList類的使用,當然,跟List類的使用基本一樣。這次的需求增加了一下,需要先打印所有的元素,然後給每個元素×2,然後打印大於10的元素。

1int[] datas = { 1, 3, 2, 5, 7, 94, 1, 8, 42, 74, 8 };
2MyList<int> list = new MyList<int>(datas);
3list.ForEach(val => Console.Write("{0}\t", val))
4  .CollectAll(val => val * 2).FindAll(val => val > 10)
5  .ForEach(val => Console.Write("{0}\t", val));

這段代碼看上去都是一些方法調用,其實它替代了許多foreach循環。不過至於後面兩個需求,使用一個foreach循環就可以辦到,上面的鏈式調用對性能有所損失。不過依據個人的愛好吧,使用哪種方式都可以。

至於Lambda表達式,其實是脫胎於代理的一個概念(至少在.Net中可以這麼說吧)。給代理賦值的時候,我們可以使用匿名函數的方式:

1ForEachAction<int> act = delegate(int val)
2{
3  //do something
4};

使用匿名函數在輸入上會有些許麻煩,如果僅僅是一小段簡單的代碼,使用匿名函數可能會顯得有點不值得。所以我們可以用Lambda表達式來達到同樣的目的,但是可以少輸入點字母:

1ForEachAction<int> act = val => {/**//*do something*/};

唠叨了這麼多,僅僅是對.Net迭代器的一些看法。也算是對在.Net中看到了Ruby式迭代器的一種興奮。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved