Convert List<object> to List<Type>, Type is known at runtime
Asked Answered
M

8

27

I am implementing some kind of deserialization and struggled with a next problem:

I have List<object> and System.Reflection.Field, it's FieldType can be List<string>, List<int> or List<bool>, so I need to convert from List<object> to that types.

public static object ConvertList(List<object> value, Type type)
{
   //type may be List<int>, List<bool>, List<string>
}

I can write each case separately, but there should be a better way using reflection.

Maurizia answered 8/4, 2014 at 14:7 Comment(0)
V
23

I believe what you want is:

public static object ConvertList(List<object> value, Type type)
{
    var containedType = type.GenericTypeArguments.First();
    return value.Select(item => Convert.ChangeType(item, containedType)).ToList();
}

Example usage:

var objects = new List<Object> { 1, 2, 3, 4 };

ConvertList(objects, typeof(List<int>)).Dump();

I'm not sure how useful this is though... It highlights the insanely useful Convert.ChangeType method I guess!


Update: Since others have correctly pointed out that this doesn't actually return a List<T> (where T is the type in question) and therefore might not fully answer the question at hand, I have chosen to provide a more up to date answer:

public static object ConvertList(List<object> items, Type type, bool performConversion = false)
{
    var containedType = type.GenericTypeArguments.First();
    var enumerableType = typeof(System.Linq.Enumerable);
    var castMethod = enumerableType.GetMethod(nameof(System.Linq.Enumerable.Cast)).MakeGenericMethod(containedType);
    var toListMethod = enumerableType.GetMethod(nameof(System.Linq.Enumerable.ToList)).MakeGenericMethod(containedType);

    IEnumerable<object> itemsToCast;

    if(performConversion)
    {
        itemsToCast = items.Select(item => Convert.ChangeType(item, containedType));
    }
    else 
    {
        itemsToCast = items;
    }

    var castedItems = castMethod.Invoke(null, new[] { itemsToCast });

    return toListMethod.Invoke(null, new[] { castedItems });
}

If you don't need the conversion (so the type of each value is actually correct, and you don't have ints in strings etc), then remove the performConversion flag and the associated block.


Example: https://dotnetfiddle.net/nSFq22

Vani answered 8/4, 2014 at 16:14 Comment(2)
This doesn't create a generic list of the specific type. It just converts the inner elements to the specific type, but the list itself is still List<object>. See: dotnetfiddle.net/Hd4y1cPiassava
Just to get rid of the magic strings "Cast" and "ToList" you could replace them with nameof(Enumerable.Cast) and nameof(Enumerable.ToList).Piassava
A
17

Not sure if this helps at all, but can you use Linq Cast?

List<object> theList = new List<object>(){ 1, 2, 3};
List<int> listAsInt = theList.Cast<int>().ToList();
Acrogen answered 8/4, 2014 at 14:14 Comment(5)
Why is this not more up-voted? It seems like the simplest solution that is managed and clean!Oui
this seems to work straight up. i would recommend this one.Hewes
theList.Cast<int>() returns an IEnumerable<int> for me, not a List<int>Prominent
good point. you would need to .ToList() after .Cast<int>()Acrogen
That's a good solution, but it does not answer to the original question - type is known at runtime, how would you deal with it?Maurizia
A
11

The type is only known at runtime so I guess generic method isn't the way to go

public static object ConvertList(List<object> value, Type type)
{
   IList list = (IList)Activator.CreateInstance(type);
   foreach (var item in value)
   {
      list.Add(item);
   }
   return list;
}
Astern answered 8/4, 2014 at 14:17 Comment(3)
yeah, it's nice solution. Found good thread here -https://mcmap.net/q/505685/-dynamic-list-lt-t-gt-type-duplicateMaurizia
I'm not sure that's going to work for the types the OP has mentioned (at least bool and int) because to create a list of objects in the first place the value types have been boxed and thus can't be added to the list. Edit: actually that's not the problem. You need to change Type[] typeArgs = { type }; in your method to Type[] typeArgs = { type.GenericTypeArguments.First() };;Vani
@Vani you are right, I wrongly expected type to be item's type. FixedAstern
B
4
    /// <summary>
    /// Converts list of items into typed list of item of new type
    /// </summary>
    /// <example><code>
    /// Consider source to be List<object>, newItemType is typeof(string), so resulting list wil have type List<string>
    /// </code></example>
    /// <param name="newItemType">New item type</param>
    /// <param name="source">List of objects</param>
    /// <returns>Typed List object</returns>
    public static IList ConvertList(Type newItemType, IList source)
    {
        var listType = typeof(List<>);
        Type[] typeArgs = { newItemType };
        var genericListType = listType.MakeGenericType(typeArgs);
        var typedList = (IList)Activator.CreateInstance(genericListType);
        foreach (var item in source)
        {
            typedList.Add(item);
        }
        return typedList;
    }
Boraginaceous answered 17/11, 2017 at 14:52 Comment(0)
M
2
 public static object ConvertList<T>(List<object> value) where T : class
    {
        var newlist = value.Cast<T>().ToList();
        return newlist;
    }

or

  public static List<T> ConvertList<T>(List<object> value) where T : class
    {
        List<T> newlist = value.Cast<T>().ToList();
        return newlist;
    }
Move answered 8/4, 2014 at 14:16 Comment(0)
S
1

Try this:

public static List<T> ConvertList<T>(List<object> list)
{
    List<T> castedList = list.Select(x => (T)x);
    return castedList;
}

Call:

List<object> myList = new List<object> {"test", "foo", "bar"};
List<string> stringList = ConvertList<string>(myList);
Sergiosergipe answered 8/4, 2014 at 14:12 Comment(1)
how can I understand that I should call ConvertList<string>? I only know Type, I can't type ConvertList<type>Maurizia
S
0
public static List<T> ConvertType<T>(List<object> list) {
    var list2 = new List<T>();
    for(int i=0;i<list.Count;i++) list2.Add((T)list[i]);
    return list2;
}
Stralka answered 8/4, 2014 at 14:13 Comment(2)
how can I understand that I should call ConvertList<string>? I only know Type, I can't type ConvertList<type>Maurizia
I see, then you'll need to use reflection to make the generic type at runtime, see Guillaume's answer (which I won't repeat).Stralka
B
0

Here is an extension method similar to the linq extension method except it takes an argument that is a type instead of a type parameter:

public static IList CastToList(this IEnumerable source, Type itemType)
{
    var listType = typeof(List<>).MakeGenericType(itemType);
    var list = (IList)Activator.CreateInstance(listType);
    foreach (var item in source) list.Add(item);
    return list;
}

Usage:

var list = new List<object>();
list.add(1);
list.add(2);
var boxedIntList = list.CastToList(typeof(int)); //type is object{List<int>}
Bowstring answered 12/1, 2021 at 6:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.