簡體   English   中英

有沒有辦法將類型參數數組傳遞給泛型方法?

[英]Is there a way to pass an array of type parameters to a generic method?

通過OpenXml,我發現了這篇文章: 如何:合並電子表格文檔中的兩個相鄰單元格(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;
}

但我不知道有什么方法可以做到這一點。

那么我的選擇是什么?

您可以為此創建一個流暢的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));
}

您正在尋找的是他們在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)

您可以實現類型列表以添加更多技巧。 例如,您可以從'tail'(其余作為Typelist)拆分'head'(typeof(T1))並使類型列表僅處理第一種類型。 使用這樣的技巧,您可以迭代列表並添加多種類型的行為。

請注意,添加了界面以便您可以添加限制,例如:

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);
    }
}

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

Reflection可以幫助您在運行時使用正確的類型調用該方法。

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 });
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM