在运行时将List<object>转换为List<Type>,Type已知

27

我正在实现某种反序列化,并遇到了一个问题:

我有一个List<object>System.Reflection.Field,它的FieldType可以是List<string>List<int>List<bool>,因此我需要将List<object>转换为这些类型。

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

我可以分别编写每个案例,但使用反射应该有更好的方法。

8个回答

23

我相信您想要的是:

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

使用示例:

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

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

我不确定这有多有用......我猜它强调了非常有用的Convert.ChangeType方法!


更新:由于其他人已经正确指出,这实际上并没有返回一个List<T>(其中T是所需的类型),因此可能不能完全回答问题,我选择提供一个更现代化的答案:

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

如果您不需要转换(因此每个值的类型实际上是正确的,而且您没有字符串中的int等),那么请删除performConversion标志及其关联块。
示例:https://dotnetfiddle.net/nSFq22

1
这不会创建一个特定类型的通用列表。它只是将内部元素转换为特定类型,但列表本身仍然是List<object>。请参见:https://dotnetfiddle.net/Hd4y1c - Oliver
1
为了摆脱魔术字符串“Cast”和“ToList”,您可以将它们替换为nameof(Enumerable.Cast)nameof(Enumerable.ToList) - Oliver

16

不确定这是否有帮助,但您可以使用Linq Cast吗?

List<object> theList = new List<object>(){ 1, 2, 3};
List<int> listAsInt = theList.Cast<int>().ToList();

2
为什么这个问题没有更多的赞?它似乎是最简单、管理和清晰的解决方案! - Arvo Bowen
这似乎直接有效。我会推荐这个。 - Jay
theList.Cast<int>() returns an IEnumerable<int> for me, not a List<int> - V15I0N
很好的建议。在.Cast<int>()之后需要使用.ToList() - DLeh
4
那是个不错的解决方案,但它并没有回答原问题——在运行时已知类型的情况下,你会如何处理? - Rustam Ganeyev

11

类型只在运行时知道,所以我猜泛型方法不是可行的方式。

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

是的,这是一个不错的解决方案。在这里找到了一个好的线程 - https://dev59.com/sHE95IYBdhLWcg3wEpro - Rustam Ganeyev
我不确定这对OP提到的类型(至少是bool和int)是否有效,因为要首先创建对象列表,值类型已被装箱,因此无法添加到列表中。编辑:实际上这不是问题。您需要在方法中将Type[] typeArgs = { type };更改为Type[] typeArgs = { type.GenericTypeArguments.First() }; - Richiban
@Richiban 你是对的,我错误地期望了 type 是项目的类型。已修复。 - Guillaume

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

2
 public static object ConvertList<T>(List<object> value) where T : class
    {
        var newlist = value.Cast<T>().ToList();
        return newlist;
    }

或者

  public static List<T> ConvertList<T>(List<object> value) where T : class
    {
        List<T> newlist = value.Cast<T>().ToList();
        return newlist;
    }

1

试试这个:

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

呼叫:
List<object> myList = new List<object> {"test", "foo", "bar"};
List<string> stringList = ConvertList<string>(myList);

1
我该如何理解我应该调用ConvertList <string>?我只知道Type,我无法输入ConvertList <type>。 - Rustam Ganeyev

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

我该如何理解我应该调用ConvertList<string>?我只知道Type,我无法键入ConvertList<type>。 - Rustam Ganeyev
我明白了,那么你需要在运行时使用反射来生成泛型类型,请参考Guillaume的答案(我不再重复)。 - kevin

0
这里有一个类似于linq扩展方法的扩展方法,不同之处在于它接受一个类型作为参数,而不是类型参数。
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;
}

用法:

var list = new List<object>();
list.add(1);
list.add(2);
var boxedIntList = list.CastToList(typeof(int)); //type is object{List<int>}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接