2013-02-19 78 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

相关问题