我開始奢望使用內建方法快速解決這個問題,後來發現不太可能。首先無法使用C# 4.0的介面協變支援,因為協變只針對參考型別,而TKey和TElement的類型可能是實值型別的。其次整個架構對IGrouping沒有提供任何直接的輔助方法。那麼將就像將IGrouping<TKey, TElement>轉換成IGrouping<object, object>就如同將IEnumerable<int>轉換成IEnumerable<object>,看似轉換是隱式的,但卻必須加入一些轉換邏輯,如下:
IEnumerable<int> intData = Enumerable.Range(1, 10);
IEnumerable<object> objData = intData.Cast<object>();
當然IGrouping<TKey, TElement>並沒有IEnumerable那麼簡單,它有兩個泛型參數,同時本身又繼承IEnumerable<TElement>。因此需要寫一個輔助類型,該類型當然必須繼承IGrouping<object, object>。同時支援從IGrouping<TKey, TElement>和Object對象的建立,後者使用反射來擷取成員值。
範例程式碼:
//將1-10按照除3的餘數來分組
IEnumerable<IGrouping<int, int>> data = Enumerable.Range(1, 10).GroupBy(i => i % 3);
foreach (IGrouping<int, int> group in data)
{
//通過輔助類型將IGrouping<int, int>轉換成IGrouping<object, object>
IGrouping<object, object> converted = IGroupingWrapper.Create(group);
//輸出資料
Console.WriteLine("除以3餘{0}", converted.Key);
foreach (object obj in converted)
Console.WriteLine(obj);
}
將會輸出:
除以3餘1
1
4
7
10
除以3餘2
2
5
8
除以3餘0
3
6
9
IGroupingWrapper類型代碼:
class IGroupingWrapper : IGrouping<object, object>
{
object key;
IEnumerable<object> collec;
IGroupingWrapper(object key, IEnumerable<object> collec)
{
this.key = key;
this.collec = collec;
}
#region IGrouping<object, object> 成員
public object Key
{
get { return key; }
}
public IEnumerator<object> GetEnumerator()
{
return collec.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
//通過IGrouping<TKey, TElement>建立IGrouping<object, object>
public static IGroupingWrapper Create<TKey, TElement>(IGrouping<TKey, TElement> igroup)
{
if (igroup == null)
throw new ArgumentNullException("igroup");
return new IGroupingWrapper(igroup.Key, igroup.Cast<object>());
}
//通過object建立IGrouping<object, object>
public static IGroupingWrapper Create(object obj)
{
if (obj == null)
throw new ArgumentNullException("obj");
return new IGroupingWrapper(obj.GetType().GetProperty("Key").GetValue(obj, new object[0]),
((IEnumerable)obj).Cast<object>());
}
}