代码之家  ›  专栏  ›  技术社区  ›  Dave Clemmer manu

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

  •  1
  • Dave Clemmer manu  · 技术社区  · 16 年前

    本质上,我正在寻找一种方法来改变 GenericList<TInput> GenericList<TOutput> 在哪里 GenericList 是实现特定接口的任何类型的泛型列表,以及 TInput TOutput 只在运行时知道。

    下面是可以执行此操作的类和方法的片段,其中 马口铁 T输出 在编译时提供。

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

    有什么方法可以沿着这条线做些什么吗? 输入端 T输出 是否可以在运行时提供?

    以一种或另一种形式使用反射似乎是实现这一目标的途径。

    最初,我尝试为 常规列表<tinput> 那需要一个类型的列表 T输出 作为参数(然后我可以调用 Activator.CreateInstance 获取新列表)。

    或者,我尝试通过反射调用上述方法,但由于该方法已被标记 ContainsGenericParameters=true ,并标记为 IsGenericMethod=false ,我无法通过 method.Invoke 或者通过一个通用的 方法调用 (无法呼叫 MakeGenericMethod )。

    3 回复  |  直到 12 年前
        1
  •  2
  •   Richard Anthony Hein    16 年前

    这不是Selectmany的全部内容吗?假设我有两个不同类型的列表,list a和listb,那么listc是一个新列表,比如:

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

    您说过直到运行时才知道这些类型,但是它们实现了一个特定的接口,所以您不必使用反射。因此,在您的情况下,lista将是一个IEnumerable,而propertya和propertyb将是您的接口公开的一些属性。

    或者,如果使用注释中提到的属性,则可以在创建匿名类型的位置使用该属性。

        2
  •  1
  •   alastairs    16 年前

    如果我正确理解了你的问题,你应该能够使用类型转换器。然而,只有当可能的tinput和toutput的列表相对较小并且遵循定义的映射时,这才真正可行。使用自定义类型转换器,可以使用标准方法can convertto、can convertfrom、convertto和convertfrom来实现所需的转换。这些方法的实现将进行必要的数据复制。

    看看这个例子, How to: Implement a Type Converter

        3
  •  0
  •   Dave Clemmer manu    12 年前

    在整理这个问题的过程中,我想我回答了我自己的问题(在其他一些帖子的帮助下),但我想我还是会把它扔到外面去。

    下面是 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 和类型列表 马口铁 作为对象。该构造函数调用另一个构造函数来创建 GenericList<TInput> .现在原始构造函数可以使用类型列表 输入端 转换为新列表。