有時會有將大量的資料做分割作業,如資料庫查尋出幾萬多筆,要以1000筆為一單位作業,相同的東西寫多了就想要簡化流程,所以就有了這個Extension。
有時會有將大量的資料做分割作業,如資料庫查尋出幾萬多筆,要以1000筆為一單位作業,相同的東西寫多了就想要簡化流程,所以就有了這個Extension。
Method的定義
public static void Split<T>(this System.Collections.Generic.IEnumerable<T> list, int batch, System.Action<IEnumerable<T>> action)
///Method2 舊版,會先將IEnumable讀過一遍,將結果存在另一個集合中,本方法至少是2N的執行次數且使用便多的記憶體
public static System.Collections.Generic.IList<IEnumerable<T>> Split<T>(this System.Collections.Generic.IEnumerable<T> list, int batch)
使用方式
與Linq的使用方法雷同,只要是IEnumable(包含List<>,DbSet<>等等)都可以切割
//Method 1
list.Split(100, sublist =>
{
//會執行10次此action
foreach (var item in sublist)
{
//something
}
});
//如果覺得寫成Lambda不好看,也可以用一般寫法
list.Split(100, ActionExtend);
private void ActionExtend(IEnumerable<int> sublist)
{
foreach (var item in sublist)
{
//something
}
}
//Method2
var batchs = list.Split(100); //會產生IEnumable<IEnumable<int>>
foreach (var batch in batchs)
{
foreach (var sublist in batch)
{
//something
}
}
原理
Method2是在去年就寫好的,因為寫起來不夠順,又覺得浪費不少記憶體空間與多了一次的迴圈執行,最近就改寫成Method1。
我建立了SplitEnumerableWarpper、SplitEnumeratorWarpper,讓它在foreach時使用原本的Enumerator只是多了一層計數器到設定的量就終止,換下一個Action繼續讀原本的Enumerator,額外的成本只有判斷式與計數器這些必要成本。
public static void Split<T>(this IEnumerable<T> list, int size, Action<IEnumerable<T>> action)
{
var e = list.GetEnumerator();
var warp = new SplitEnumerableWarpper<T>(e, size);
while (warp.Next()) //有下一個批次就執行Action
{
action(warp);
}
}
//SplitEnumerableWarpper
public bool Next()
{
//判斷還有沒有下一個批次
if (_enumeratorWarp == null)
{
//第一次執行
_enumeratorWarp = new SplitEnumeratorWarpper<T>(_enumerator, _size);
return true;
}else if (_enumeratorWarp.MoveResult)
{
//_enumerator還可以繼續執行
_enumeratorWarp = new SplitEnumeratorWarpper<T>(_enumerator, _size);
return true;
}
else
{
//原_enumerator已經結束了
return false;
}
}
//SplitEnumeratorWarpper
public bool MoveNext()
{
包裝原本Enumerator的MoveNext,超過計數就結束
if (this.MoveCount < this._size)
{
this.MoveResult = this._enumerator.MoveNext();
if (this.MoveResult)
{
this.MoveCount++;
}
return this.MoveResult;
}
return false;
}
註:Enumerator是.Net中為了foreach而延伸的類,有二個主要成員
MoveNext: 往下巡覽,可回傳True,不可回傳False,當False時結束foreach
Current: 當前值
詳情請參考:http://msdn.microsoft.com/zh-tw/library/system.collections.ienumerator.aspx
下載IterateExtension.cs,同檔中包含更之前寫的萬用String Join使用IEnumerable Extension
2011-08-15:
朋友說有一個小問題,如長度0的集合,也會進Action,因為原本必需要在Action中使用到MoveNext(),才會知道集合有沒有結束,所以改成進集合前會先執行一次MoveNext(),如果不能往下就不執行Action。