将一种类型的通用列表转换为另一种类型,其中类型仅在运行时已知

时间:2009-11-17 22:14:52

标签: c# generics reflection

基本上,我正在寻找一种将GenericList<TInput>转换为GenericList<TOutput>的方法,其中GenericList是实现特定接口的任何类型的通用列表,以及类型{{ 1}}和TInput仅在运行时已知。

下面是一个可以执行此操作的类和方法的片段,其中TOutputTInput在编译时提供。

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,因此我无法通过正常{调用{ {1}}或通过通用IsGenericMethod=false(无法调用method.Invoke)。

3 个答案:

答案 0 :(得分:2)

这不是SelectMany的全部意义吗?假设我有两个不同类型的列表,listA和listB,那么listC是一个新列表,如:

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

你说你直到运行时才知道类型,但它们实现了特定的接口,因此你不必使用反射。所以在你的情况下,listA将是一个IEnumerable而PropertyA和PropertyB将是你的界面暴露的一些属性。

或者,如果您正在使用评论中提到的属性,则可以使用创建匿名类型的属性。

答案 1 :(得分:1)

如果我已正确理解您的问题,您应该可以使用类型转换器。但是,如果可能的TInput和TOutput列表相对较小并且遵循定义的映射,这将真正可行。使用自定义类型转换器,您可以使用标准方法CanConvertTo,CanConvertFrom,ConvertTo和ConvertFrom来实现所需的转换。您对这些方法的实现将进行必要的数据复制。

查看示例How to: Implement a Type Converter

答案 2 :(得分:0)

在将这个问题放在一起的过程中,我想我回答了我自己的问题(在这里的其他一些帖子的帮助下),但我想我还是会把它扔出去。

以下是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的列表转换为新列表。