如何将IEnumerable <object>强制转换为IEnumerable <runtime type =“”>

时间:2015-08-18 19:54:50

标签: c# .net linq generics casting

我试图完成以下操作。

假设我有这个数据模型:

public class Article
{
     public ICollection<string> Tags { get; set; }
}

从数据库中检索这些标记。我的数据库API将它们作为List<object>返回给我。

因此,我需要从List<object>转换为实现ICollection<string>的内容。

我知道LINQ Cast<T>()方法将其元素强制转换为给定类型并返回转换后的IEnumerable<T>

但是,我无法使用Cast<string>(),因为这会始终将List<object>投放到IEnumerable<string>,而不会为具有ICollection<double>属性(或任何其他类型)的模型提供任何选项)。

我可以使用反射并获取泛型类型参数:

Type genericArg = collectionType.GetGenericArguments().First();

但这会让我得到一个运行时Type,我不能将其用作Cast<genericArg>()

如何将IEnumerable<object>投射到动态IEnumerable的{​​{1}}?

我应该注意,我的模型上不允许使用复杂的类型,所以像:

Type

不会发生。我只处理原始类型。

4 个答案:

答案 0 :(得分:7)

你对铸造有一个基本的误解。

必须在编译时知道转换操作的结果类型。¹

考虑以下示例:

string a = "abc";
object b = (object)a;
string c = (string)b;

abc运行时类型是相同的。它是string编译时类型是不同的。转换仅与编译时类型相关。

因此,你的问题的答案

  

如何将IEnumerable<object>投射到IEnumerable<runtime type>

是:你没有。对于运行时类型,转换没有意义。

那就是说,让我为你的真正问题提供一个解决方案:假设你有一个IEnumerable<object> values,一个Type myTargetType,并希望创建一个包含值的List<typeof(myTargetType)>

首先,使用反射创建列表:

var listType = typeof(List<>).MakeGenericType(myTargetType);
IList myList = (IList)Activator.CreateInstance(listType);

然后填写清单:

foreach (var item in values)
{
    myList.Add(item);
}

显然,如果Add的条目不是的运行时类型valuesmyTargetTypethrow an ArgumentException

¹结果类型可以是泛型类型,但是generic type parameters have to be specified at compile time as well

答案 1 :(得分:1)

我相信System.Convert有你需要的东西:

Type genericArg = collectionType.GetGenericArguments().First();
foreach(var obj in collection) {
    yield return Convert.ChangeType(obj, genericArg);
}

答案 2 :(得分:0)

class Program { static void Main(string[] args) { var source = new List<object> { "foo", "bar", "baz" }; var type = typeof(string); // or however you find out the type var castMethod = typeof(Enumerable) .GetMethod("Cast").MakeGenericMethod( new[] { type }); var result = (IEnumerable<string>) castMethod.Invoke(null, new object[] {source}); foreach (var str in result) { Console.WriteLine(str.ToUpper()); } } } 通常是您正在寻找的。如果需要不同的变化,可以使用反射来自己关闭泛型类型:

List<T>

另一个问题是,从一个IEnumerable<T>转换到另一个{ - >不一致 - 通用参数是不变,因为该集合是读写的。 (由于历史原因,数组允许进行一些此类转换。)但是,如果您只是阅读,则从Cast返回的client.emailWithTemplate("jenny@example.com", "bob@example.com",<template-id>, { "link" : "https://example.com/reset?key=secret", "recipient_name" : "Jenny" }); 就足够了。

答案 3 :(得分:0)

您需要实现一个通用方法,该方法从数据库api中获取结果并根据您的模型返回适当的集合,如下所示:

private ICollection<T> RetrieveTags()
{
      // Get tags using database api

      return tags.Cast<T>();
 }

然后调用此方法以根据需要获取模型,例如:

ICollection<int>  t1 = RetrieveTags<int>();
ICollection<string>  t2 = RetrieveTags<string>();