我试图完成以下操作。
假设我有这个数据模型:
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
不会发生。我只处理原始类型。
答案 0 :(得分:7)
你对铸造有一个基本的误解。
必须在编译时知道转换操作的结果类型。¹
考虑以下示例:
string a = "abc";
object b = (object)a;
string c = (string)b;
a
,b
和c
的运行时类型是相同的。它是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
的条目不是的运行时类型values
,myTargetType
将throw 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>();