Преобразование универсального списка одного типа в другой тип, где типы известны только во время выполнения - PullRequest
1 голос
/ 18 ноября 2009

По сути, я ищу способ преобразования GenericList<TInput> в GenericList<TOutput>, где GenericList - это общий список любого типа, который реализует определенный интерфейс, а типы TInput и TOutput известно только во время выполнения.

Ниже приведен фрагмент класса и метода, который может выполнять эту операцию, где TInput и TOutput предоставляются во время компиляции.

 // --------------------------------------------------------------------------------
 /// <summary>This class is an example snippet for transforming generic lists of different types.</summary>
 ///
 /// <remarks></remarks>
 // --------------------------------------------------------------------------------
 public abstract class GenericListHelper<TInput, TOutput>
  where TInput : IGenericObject, new()
  where TOutput : IGenericObject, new()
 {

  // --------------------------------------------------------------------------------
  /// <summary>This method takes in a generic list of an input type
  /// and transforms it a list of the output type.</summary>
  /// 
  /// <param name="inputGenericList">The input list to transform to this list.</param>
  /// <param name="filterElements">Field and property values to exclude from transform.</param>
  // --------------------------------------------------------------------------------
  public static GenericList<TOutput> CreateList(GenericList<TInput> inputGenericList, NameObjectCollection filterElements)
  {
   if (inputGenericList != null)
   {
    GenericList<TOutput> outputGenericList = new GenericList<TOutput>();
    foreach (TInput loopItem in inputGenericList)
    {
     TOutput newItem = new TOutput();
     DataTransformHelper.TransformDataFromObject(loopItem, newItem, filterElements);
     outputGenericList.Add(newItem);
    }
    return outputGenericList;
   }
   return null;
  }
 }

Есть ли способ сделать что-то в этом духе, когда TInput и TOutput могут быть предоставлены во время выполнения?

Использование отражения в той или иной форме, кажется, путь к этому.

Изначально я попытался создать конструктор для GenericList<TInput>, который бы принимал список типа TOutput в качестве параметра (тогда я мог бы вызвать Activator.CreateInstance, чтобы получить новый список).

В качестве альтернативы я попытался вызвать вышеупомянутый метод с помощью отражения, но так как этот метод помечен ContainsGenericParameters=true и помечен как IsGenericMethod=false, я не смог вызвать метод, либо через обычный method.Invoke, либо через универсальный method.Invoke (невозможно позвонить MakeGenericMethod).

Ответы [ 3 ]

2 голосов
/ 18 ноября 2009

Не в этом ли суть SelectMany? Скажем, у меня есть два списка разных типов, listA и listB, затем listC - это новый список, например:

var listC = listA.SelectMany(a => listB, (a, b) => new { a.PropertyA, b.PropertyB });

Вы сказали, что не знаете типы до времени выполнения, но они реализуют определенный интерфейс, поэтому вам не нужно использовать отражение. Таким образом, в вашем случае listA будет IEnumerable, а PropertyA и PropertyB будут некоторыми свойствами, предоставляемыми вашим интерфейсом.

Или, если вы используете атрибуты, как вы упомянули в комментарии, вы можете использовать их там, где создается анонимный тип.

1 голос
/ 18 ноября 2009

Если я правильно понял вашу проблему, вы сможете использовать преобразователи типов. Однако это действительно будет возможно, только если списки возможных TInput и TOutput относительно малы и соответствуют определенному отображению. Используя специальный конвертер типов, вы можете использовать стандартные методы CanConvertTo, CanConvertFrom, ConvertTo и ConvertFrom для достижения требуемых преобразований. Ваши реализации этих методов сделают необходимое копирование данных.

Посмотрите пример, Как: реализовать преобразователь типов

0 голосов
/ 09 ноября 2011

В процессе составления этого вопроса, я думаю, что я ответил на свой собственный вопрос (с помощью некоторых других постов здесь), но я думал, что все равно выкину это.

Ниже приведен фрагмент некоторых конструкторов для GenericList, чтобы помочь с преобразованием (статический метод выше не используется в этом процессе).

 // --------------------------------------------------------------------------------
 /// <summary>This class is used for strongly typed sortable lists of generic
 /// objects (such as data access or business objects).</summary>
 ///
 /// <remarks></remarks>
 // --------------------------------------------------------------------------------
 public class GenericList<T> : IGenericList<T>
           where T : IGenericObject, new()
 {

  // --------------------------------------------------------------------------------
  /// <summary>Base constructor.</summary>
  // --------------------------------------------------------------------------------
  public GenericList()
  {
  }

  // --------------------------------------------------------------------------------
  /// <summary>This constructor takes in a generic list of the same
  /// type and transforms it to this list.</summary>
  /// 
  /// <param name="inputGenericList">The input list to transform to this list.</param>
  /// <param name="filterElements">Field and property values to exclude from transform.</param>
  // --------------------------------------------------------------------------------
  public GenericList(GenericList<T> inputGenericList, NameObjectCollection filterElements)
  {
   if (inputGenericList != null)
   {
    foreach (T loopItem in inputGenericList)
    {
     T newItem = new T();
     DataTransformHelper.TransformDataFromObject(loopItem, newItem, filterElements);
     Add(newItem);
    }
   }
  }

  // --------------------------------------------------------------------------------
  /// <summary>This constructor takes in a generic list of another
  /// type and transforms it to this list.</summary>
  /// 
  /// <param name="inputListElementType">The type of element to be found in the input list.</param>
  /// <param name="inputGenericList">The input list to transform to this list.</param>
  /// <param name="filterElements">Field and property values to exclude from transform.</param>
  // --------------------------------------------------------------------------------
  public GenericList(Type inputListElementType, object inputGenericList, NameObjectCollection filterElements)
  {
   if (inputGenericList != null)
   {
    Type inputListType = typeof(GenericList<>);
    Type combinedType = inputListType.MakeGenericType(inputListElementType);
    IList elements = (IList) Activator.CreateInstance(combinedType, inputGenericList, filterElements);
    foreach (IGenericObject loopItem in elements)
    {
     T newItem = new T();
     DataTransformHelper.TransformDataFromObject(loopItem, newItem, filterElements);
     Add(newItem);
    }
   }
  }
 }

Итак, вызывающий код вызывает Activator.CreateInstance для создания экземпляра GenericList<TOutput>, вызывая указанный выше конструктор, который принимает тип TInput и список типа TInput в качестве объекта. Этот конструктор вызывает другой конструктор для создания экземпляра GenericList<TInput>. Теперь оригинальный конструктор может использовать список типа TInput для преобразования в новый список.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...