2013-02-19 88 views
4

通過OpenXml工作,我遇到了這篇文章:How to: Merge two adjacent cells in a spreadsheet document (Open XML SDK)有沒有辦法將一個類型參數數組傳遞給泛型方法?

那裏有一個代碼示例,我想重構。下面是它的一部分:

// Insert a MergeCells object into the specified position. 
if (worksheet.Elements<CustomSheetView>().Count() > 0) 
{ 
    worksheet.InsertAfter(mergeCells, 
          worksheet.Elements<CustomSheetView>().First()); 
} 
else if (worksheet.Elements<DataConsolidate>().Count() > 0) 
{ 
    worksheet.InsertAfter(mergeCells, 
          worksheet.Elements<DataConsolidate>().First()); 
} 
else if (worksheet.Elements<SortState>().Count() > 0) 
{ 
    worksheet.InsertAfter(mergeCells, 
          worksheet.Elements<SortState>().First()); 
} 
//...and 5 more 

我管理的最好的事情是一個擴展方法:

public static bool InsertElementAfter<T>(this Worksheet worksheet, 
               OpenXmlElement element) 
    where T : OpenXmlElement 
{ 
    if (!worksheet.Elements<T>().Any()) 
     return false; 
    else 
    { 
     worksheet.InsertAfter(element, worksheet.Elements<T>().First()); 
     return true; 
    } 
} 

但它的使用看起來原始代碼儘可能多的可怕:

if (!worksheet.InsertElementAfter<CustomSheetView>(mergeCells)) 
    if (!worksheet.InsertElementAfter<DataConsolidate>(mergeCells)) 
     if (!worksheet.InsertElementAfter<SortState>(mergeCells)) 
      //...and 5 more 

如果我可以以某種方式聲明一個類型參數的數組(或某物),我可以這樣寫:

foreach (var T in typeParameterList) 
{ 
    if (worksheet.InsertElementAfter<T>(mergeCells)) 
     break; 
} 

但我不知道有什麼辦法做到這一點。

那麼我有什麼選擇?

回答

4

您可以爲此創建一個流暢的API。其結果可能讓這樣的代碼:

worksheet.InsertAfter<CustomSheetView>(mergeCells) 
     .Or<DataConsolidate>() 
     .Or<SortState>(); 

有兩個優勢,這種流暢的API:

  1. 這是非常傳神
  2. 它的工作原理沒有反映。

API的實現將需要保存的值和Or()方法的類:

public class ChainedElementInserter 
{ 
    OpenXmlElement _element; 
    Worksheet _worksheet; 
    bool _previousResult; 

    // ctor that initializes all three fields goes here. 

    public ChainedElementInserter Or<T>() 
     where T : OpenXmlElement 
    { 
     if (!_previousResult) 
      _previousResult = _worksheet.InsertElementAfter<T>(_element); 

     return this; 
    } 
} 

InsertAfter擴展方法開始這條產業鏈,看起來像這樣:

public static ChainedElementInserter InsertAfter<T>(this Worksheet worksheet, 
            OpenXmlElement element) 
    where T : OpenXmlElement 
{ 
    return new ChainedElementInserter(
     worksheet, element, worksheet.InsertElementAfter<T>(element)); 
} 
+0

非常感謝。你給了我很大的推動力,因爲我從來沒有想過要這樣做。您的答案確實有機會避免使用反思並縮短代碼而不會有任何冗餘! – horgh 2013-02-19 23:38:33

+0

我讓自己編輯你的答案,以顯示我使用你的代碼的方式。希望你同意這些修改。再次感謝你。 – horgh 2013-02-19 23:41:42

+0

@KonstantinVasilcov:很好。現在它更短了。 – 2013-02-20 06:25:01

3

你在找什麼是他們在C++中調用的「類型列表」。但是,這些不幸在C#中不受支持。

你可以做的是通過創建一堆各種類型參數的類,使模擬這種行爲,他們「遞歸」如下:

public interface ITypelist { Type[] List { get; } } 
public class Typelist<T1> : ITypelist { 
    public Type[] List { get { return new Type[]{typeof(T1)}; }} 
} 
public class Typelist<T1, T2> : ITypelist { 
    public Type[] List { get { return new Type[]{typeof(T1), typeof(T2)}; }} 

} 
// etc 

然後,你可以用它來傳遞的類型的列表:

worksheet.InsertElementAfter<Typelist<T1, T2, T3>>(mergeCells) 

你可以實現類型列表來添加更多的詭計。例如,您可以將'head'(typeof(T1))從'tail'(其餘部分作爲Type列表)分開,並使該列表僅處理第一個類型。使用這樣的技巧,您可以迭代列表併爲多種類型添加行爲。

注意接口加入,這樣就可以增加一個限制,如:

void InsertElementAfter<T>(...) where T:ITypelist 

...不幸的是,你不能傳遞方法和類型爲「通用」的方法(?還),所以最好你可以做的是將它們作爲字符串傳遞,並使用反射使它成爲一個'真正的'方法(使用MakeGenericType/...)。

最終你會最終有一個大的助手類,它看起來是這樣的:

// ... 

public class Typelist<T1, T2> : ITypelist 
{ 
    public Type MakeGenericType(Type t) 
    { 
     return t.MakeGenericType(typeof(T1)); 
    } 

    public MethodInfo MakeGenericMethod(MethodInfo method) 
    { 
     return method.MakeGenericMethod(typeof(T1)); 
    } 

    public Type Head { get { return typeof(T1); } } 
    public Typelist<T2> Tail { get { return new Typelist<T2>(); } } 

    public Type[] List { get { return new Type[] { typeof(T1), typeof(T2) }; } } 
} 

// ... 

public static class Ext 
{ 
    public static void InvokeAll<T1>(this Typelist<T1> typelist, MethodInfo baseMethod, object obj, object[] pars) 
    { 
     typelist.MakeGenericMethod(baseMethod).Invoke(obj, pars); 
     // tail so no recursion 
    } 

    public static void InvokeAll<T1, T2>(this Typelist<T1, T2> typelist, MethodInfo baseMethod, object obj, object[] pars) 
    { 
     typelist.MakeGenericMethod(baseMethod).Invoke(obj, pars); 
     InvokeAll(typelist.Tail, baseMethod, obj, pars); 
    } 
} 

問題是,如果這是一個好主意或不...的附加值是可以利用的類型系統,並獲得通過泛型傳遞類型列表的能力,缺點是你有很多代碼,仍然必須使用反射。

+0

這對於這種任務看起來太複雜了。不過謝謝你的努力 – horgh 2013-02-19 23:43:07

+0

我會高興的,如果它不僅僅是巧妙的僞裝反映。 – 2013-02-20 06:28:23

+0

@DanielHilgarth是的,這也是我自己的結論:-)嗯,問題是要通過多種類型,這正是我所做的。我實際上是在考慮使用動態,這也是一種反思......最終它並沒有變得更好。類型列表在C#中不受支持。唯一值得商榷的是,如果僞裝泛型之後的反射有價值......我的觀點是解釋如何傳遞類型列表,如果你做了什麼,以及在語言中需要什麼才能正常工作 - 在C++中,不僅僅是反思,在這裏不幸的是它不是。 – atlaste 2013-02-20 06:50:13

2

反射可以幫助您在運行時調用正確類型的方法。

Type[] typeParamList = new Type[] { typeof(CustomSheetView), typeof(DataConsolidate) } //And 9 more 

MethodInfo method = typeof(Extensions).GetMethod("InsertElementAfter"); 
foreach (var type in typeParamList) 
{ 
    var genericMethod = method.MakeGenericMethod(new Type[] { type }); 
    genericMethod.Invoke(null, new object[] { worksheet, mergeCells }); 
} 
+0

如果你不明白,我沒有'typeParameterList';這是一個僞集合,我正在問關於 – horgh 2013-02-19 09:48:53

+0

我不想粗魯,但你將不得不爲自己思考一下。我的代碼是僞造的,你不能複製粘貼它。但生病嘗試重寫它,以便您可以將其粘貼到那裏。給我一分鐘 – Evelie 2013-02-19 09:53:17

+1

Ehm ..所以讀一遍又一遍..不,我真的不明白你在做什麼。爲什麼不只是聲明你正在談論的類型參數列表。這8種類型似乎已經知道了?新類型[] {typeof(CustomSheetView)} – Evelie 2013-02-19 10:01:26

相關問題