
[英]How could I declare list of generics with different and unknown types and How could I initialize a generic with a type only known at runtime?
[英]Transforming a generic list of one type to another type, where the types are only known at runtime
本质上,我正在寻找一种将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
调用该method.Invoke
(无法调用MakeGenericMethod
)。
这不是SelectMany的全部内容吗? 假设我有两个不同类型的列表,即listA和listB,那么listC是一个新列表,例如:
var listC = listA.SelectMany(a => listB, (a, b) => new { a.PropertyA, b.PropertyB });
您说过直到运行时才知道类型,但它们实现了特定的接口,因此您不必使用反射。 因此,在您的情况下,listA将是IEnumerable,PropertyA和PropertyB将是您的接口公开的某些属性。
或者,如果您使用的是注释中提到的属性,则可以在创建匿名类型的地方使用该属性。
如果我正确理解了您的问题,则应该可以使用类型转换器。 但是,这仅在可能的TInput和TOutput的列表相对较小且遵循定义的映射的情况下才切实可行。 使用自定义类型转换器,可以使用标准方法CanConvertTo,CanConvertFrom,ConvertTo和ConvertFrom来实现所需的转换。 这些方法的实现将进行必要的数据复制。
查看示例, 如何:实现类型转换器
在把这个问题放在一起的过程中,我想我回答了我自己的问题(在这里其他一些帖子的帮助下),但是我想我还是会把这个问题扔掉。
下面是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>
的实例, GenericList<TOutput>
调用上面的构造方法,该构造方法将TInput
类型和TInput
类型列表作为对象。 该构造函数调用另一个构造函数来创建GenericList<TInput>
的实例。 现在,原始构造函数可以使用TInput
类型的列表转换为新列表。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.