Essentially, I'm looking for a way to transform GenericList<'TInput> to GenericList<'TOutput>, where GenericList is a generic list of any type that implements a specific interface, and the types TInput and TOutput are only known at runtime.
Below is a snippet of a class and method that can perform this operation, where TInput and TOutput are supplied at compile time.
// --------------------------------------------------------------------------------
/// <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;
}
}
Is there any way of doing something along these lines where TInput and TOutput can be supplied at runtime?
Using reflection in one form or another seems to be the path to get there.
Initially, I tried creating a constructor for GenericList<'TInput> that would take a list of type TOutput as a parameter (then I could call Activator.CreateInstance to get the new list).
Alternatively, I tried invoking the above method through reflection, but since that method is flagged ContainsGenericParameters=true, and is flagged as IsGenericMethod=false, I was not able to invoke the method, either through a normal method.Invoke or through a generic method.Invoke (unable to call MakeGenericMethod).
Well, in the process of putting this question together, I think I answered my own question (with help from some other posts here), but I thought I'd throw this out there anyway.
Below is a snippet of some constructors for a GenericList to help with the transformation (the static method above is not used in this process).
// --------------------------------------------------------------------------------
/// <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);
}
}
}
}
So, the calling code invokes Activator.CreateInstance to create an instance of GenericList<'TOutput>, calling the constructor above that takes the type TInput and list of type TInput as an object. That constructor invokes the other constructor to create an instance of GenericList<'TInput>. Now the original constructor can use the list of type TInput to transform into the new list.
This seems to work, though I'm not sure how well it performs. Are there other good ways of doing this? Thanks!