Cast(强制类型转换)和Convert(类型转换)是不同的。将 Int32
转换为 Int64
是一种语义无损转换。许多C#类型会重载 '(type)' 强制类型转换操作符并使用自定义转换运算符执行类型转换,此时不能使用类型转换。
C#语言会为常见且安全的代码模式生成隐式转换。
int num = 2147483647;
long bigNum = num;
但这些是例外,规则是类型转换需要将对象分配给目标类型的变量。另一个例外是,object
类型的表达式可以强制转换为任何类型,并且如果该对象无法赋值给该类型的变量,则在运行时失败。
Enumerable.Cast<T>
会对集合中的每个元素执行 Cast 操作。这不是一种转换,即使类型实现了用户定义的转换运算符也是如此。这是因为强制转换是从 object
到 T
,从而绕过任何用户定义的转换运算符或 C# 隐式转换。
来源:
public static IEnumerable<TResult> Cast<TResult>(this IEnumerable source)
{
IEnumerable<TResult> typedSource = source as IEnumerable<TResult>;
if (typedSource != null) return typedSource;
if (source == null) throw Error.ArgumentNull("source");
return CastIterator<TResult>(source);
}
static IEnumerable<TResult> CastIterator<TResult>(IEnumerable source) {
foreach (object obj in source) yield return (TResult)obj;
}
参考源代码
请注意,Cast<T>
的参数是 Enumerable
,而不是 Enumerable<TSource>
。此 API 的目的是为非泛型集合提供适配器。在 .NET 中引入泛型之前(请注意,它们不仅仅是一种语言特性),Enumerable
是基础集合类型,要使用旧的集合来进行 LINQ 操作,需要将它们转换为 IEnumerable<T>
。
即使这个 API 的目标是应用用户定义的转换或隐式转换,C# 中也没有明显的方法来实现它。C# 泛型不是模板*,并且该泛型方法将有一个单独的实现,必须遵循 C# 的规则。例如:
public static IEnumerable<TDest> MyCast<TDest, TSource>(this IEnumerable<TSource> col)
{
foreach (var s in col)
yield return (TDest)s;
}
编译失败,因为编译器无法验证 TDest 是否能够从 TSource 类型的对象中赋值。所以您需要先将每个项目转换为 object
,这正是接受非泛型 IEnumerable 的版本所发生的。
您可以尝试编写以下代码:
yield return (TDest)Convert.ChangeType(s, typeof(TDest));
然后,这个方法应该是:
public static class MyConvertExtension
{
public static IEnumerable<TDest> Convert<TDest, TSource>(this IEnumerable<TSource> col)
{
foreach (var s in col)
yield return (TDest)Convert.ChangeType(s, typeof(TDest));
}
}
*C++模板和C#泛型之间的区别
C++允许在模板中使用可能不适用于所有类型参数的代码,然后再针对具体使用的类型参数进行检查。而C#要求类中的代码必须以一种能够适用于满足其约束条件的任何类型的方式编写。例如,在C++中,可以编写一个函数,该函数对类型参数的对象使用算术运算符+和-,但如果使用不支持这些运算符的类型实例化模板,则会产生错误。C#不允许此操作,仅允许使用那些可以从约束条件推导出来的语言构造。