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

If I мог бы каким-то образом объявить массив (или что-то) параметров типа, я мог бы написать что-то вроде этого:

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

+0

Позвольте себе отредактировать ваш ответ, чтобы показать, как я использовал ваш код. Надеюсь, вы согласитесь с этими изменениями. Еще раз спасибо. – horgh

+0

@ Константин Васильков: Ницца. Теперь он еще короче. –

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» (остальное как Typelist) и сделать список типов только обработкой первого типа. Используя трюки, вы можете перебирать список и добавлять поведение для нескольких типов.

Обратите внимание, что интерфейс будет добавлен, так что вы можете добавить ограничение, например:

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

+0

Я бы поднял его, если бы это было больше, чем просто отражение, смутно замаскированное. –

+0

@ DanielHilgarth Да, это был мой собственный вывод :-) Ну, вопрос состоял в том, чтобы передать несколько типов, и это именно то, что я делаю. Я на самом деле думал об использовании динамических, что также является своего рода отражением ... в конечном итоге это не намного лучше. В C# администраторы просто не поддерживаются. Остается только спорным, если замаскировать отражение после того, как дженерики имеют значение ... Я хотел бы объяснить, как вы можете передавать списки типов, что произойдет, если вы это сделаете, и что требуется на языке для правильной работы - на C++ это это не просто отражение, но, к сожалению, это не так. – atlaste

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

+0

Я не хочу быть грубым, но вам придется немного подумать о себе. Мой код был psuedo aslo, вы не можете просто скопировать его. Но плохо попробуйте переписать его, чтобы вы могли просто вставить его туда. Дайте мне минутку – Evelie

+1

Ehm .. Так что читайте снова и снова. Нет. Я действительно не понимаю, что вы после. Почему бы вам просто не объявить этот список параметров типа, о котором вы говорите. Как известно, 8 типов уже известны? new Тип [] {typeof (CustomSheetView)} – Evelie

Смежные вопросы