如何将 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>
的计划注定要失败。这些项目将是盒装整数。装箱的整数不能拆箱为字符串。
看来您是在与类型系统作斗争,而不是使用它。也许你应该描述你真正的问题,而不是你试图围绕类型系统做一个结束-运行来解决它。
此问题已完全编辑。要查看原始版本,请查看编辑历史记录
我想我应该解释一下我的情况和问题的背景。
我正在检查用自定义属性注释的 属性 类型。然后我得到那个 属性 的值,但是作为 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>
的计划注定要失败。这些项目将是盒装整数。装箱的整数不能拆箱为字符串。
看来您是在与类型系统作斗争,而不是使用它。也许你应该描述你真正的问题,而不是你试图围绕类型系统做一个结束-运行来解决它。