如何将IEnumerable <! - ? - >转换为IEnumerable <string>?

时间:2015-12-11 19:01:03

标签: c# .net linq generics covariance

此问题已完全修改。要查看原始版本,请参阅编辑记录

我想我应该解释一下我的情况和问题的背景。

我正在检查使用自定义属性注释的属性的类型。然后我获得该属性的值,但是作为object

IEnumerable<object> propertiesValues = myType.GetProperties().Where(p => p.GetCustomAttributes(typeof(MyCustomAttribute)) != null);

一旦我拥有了这些值,我想在它们上调用我的方法Map,根据它们的类型将它们转换为不同的对象。这是因为我的数据库API需要这样。

foreach(object value in propertiesValues)
{
     object mapped = Map(value);
     // ...
}

我需要将所有IEnumerable<NumberType>值转换为IEnumerable<string>。所以我的第一个方法是:

public static object Map(object value)
{
    if(value is IEnumerable<short> || value is IEnumerable<int> || ....)
         return ((IEnumerable<object>) value).Cast<string>();
}

但是,这会引发运行时异常,因为我无法将IEnumerable<int>强制转换为IEnumerable<object>

2 个答案:

答案 0 :(得分:13)

您想要的功能称为协方差,只有当类型参数都是引用类型时,C#才支持IEnumerable<T>。也就是说,IEnumerable<string>可能会转换为IEnumerable<object>,但IEnumerable<int>可能不会如此转换。

原因是:当您获得一系列字符串时,这些字符串引用是已经合法对象引用。但是当一个int被装箱时,它只会成为一个合法的对象引用;编译器在哪里可以插入装箱操作?它没有,所以转换是非法的。

要将IEnumerable<int>转换为IEnumerable<object>,您必须通过投影明确地进行装箱:

from number in items select (object)item

items.Select(item => (object) item)

那将是IEnumerable<object>,然后你就可以做你喜欢的事了。

或使用Cast序列运算符

items.Cast<object>()

做同样的事情。或使用其查询表单:

from object item in items select item

或者只是使用非泛型IEnumerable,它会为您提供一系列盒装值,如果它实际上是一系列结构。

我注意到您通过IEnumerable<object>扩展方法将IEnumerable<string>投射到Cast的计划注定要失败。这些物品将是盒装的。盒装的int不能拆箱到字符串。

看起来你在这里打击类型系统而不是使用它。也许你应该描述你真正的问题,而不是你试图围绕类型系统进行最终运行来解决它。

答案 1 :(得分:2)

你为什么不这样做

<a class="twitter-timeline"
  data-widget-id="600720083413962752"
  href="https://twitter.com/TwitterDev"
  width="300"
  height="300">
Tweets by @TwitterDev
</a>

您也可以更新var source = ... /// IEnumerable<???> var destination = source.Select(item => item.ToString()); 的签名:

Map

object Map<TItem, TSource>(TSource source) 
  : where TSource : IEnumerable<TItem>