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

How can I cast IEnumerable<?> to IEnumerable<string>?

此问题已完全编辑。要查看原始版本,请查看编辑历史记录


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

我正在检查用自定义属性注释的 属性 类型。然后我得到那个 属性 的值,但是作为 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>

你为什么不这样做

var source = ... /// IEnumerable<???>
var destination = source.Select(item => item.ToString());

您也可以更新 Map 的签名:

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

object Map<TItem>(IEnumerable<TItem> source) 

您想要的功能称为 covariance,并且仅当类型参数都是引用类型时 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,如果实际上它是一个结构序列,它会为您提供一个装箱值序列。

我注意到你通过 Cast 扩展方法将 IEnumerable<object> 转换为 IEnumerable<string> 的计划注定要失败。这些项目将是盒装整数。装箱的整数不能拆箱为字符串。

看来您是在与类型系统作斗争,而不是使用它。也许你应该描述你真正的问题,而不是你试图围绕类型系统做一个结束-运行来解决它。