Mat*_*eer 12 .net c# generics casting .net-4.0
为什么这是编译时错误?
public TCastTo CastMe<TSource, TCastTo>(TSource i)
{
return (TCastTo)i;
}
Run Code Online (Sandbox Code Playgroud)
错误:
annot将类型'TSource'转换为'TCastTo'
为什么这是一个运行时错误?
public TCastTo CastMe<TSource, TCastTo>(TSource i)
{
return (TCastTo)(object)i;
}
int a = 4;
long b = CastMe<int, long>(a); // InvalidCastException
// this contrived example works
int aa = 4;
int bb = CastMe<int, int>(aa);
// this also works, the problem is limited to value types
string s = "foo";
object o = CastMe<string, object>(s);
Run Code Online (Sandbox Code Playgroud)
我搜索了SO和互联网以获得答案,并找到了类似的通用相关铸造问题的许多解释,但我找不到任何关于这个特殊的简单案例.
Eri*_*ert 24
为什么这是编译时错误?
问题是每种可能的值类型组合对于强制转换的含义都有不同的规则.将64位双精度转换为16位int与将十进制转换为浮点数完全不同,依此类推.可能性的数量是巨大的.所以想像编译器一样.编译器应该为您的程序生成什么代码?
编译器必须生成在运行时再次启动编译器的代码,对类型进行全新分析,并动态发出适当的代码.
这看起来似乎可能比你预期的泛型更多的工作和更少的性能,所以我们只是取缔它.如果您真正想要的是编译器再次启动并对类型进行分析,请在C#4中使用"dynamic"; 这就是它的作用.
为什么这是一个运行时错误?
同样的道理.
由于与上述相同的原因,盒装的int只能解包为int(或int?); 如果CLR尝试从盒装值类型到每个其他可能的值类型进行所有可能的转换,那么基本上它必须在运行时再次运行编译器.这将出乎意料地缓慢.
那么为什么它不是参考类型的错误?
因为每个引用类型转换与每个其他引用类型转换相同:您询问对象以查看它是从所需类型派生还是与所需类型相同.如果不是,则抛出异常(如果进行强制转换)或结果为null/false(如果使用"as/is"运算符).规则对于引用类型是一致的,它们不是值类型.记住引用类型知道自己的类型.价值类型没有; 对于值类型,执行存储的变量是唯一知道适用于这些位的类型语义的东西.值类型包含其值,不包含其他信息.引用类型包含其值以及大量额外数据.
有关更多信息,请参阅我关于此主题的文章:
http://ericlippert.com/2009/03/03/representation-and-identity/
C#对多个不同的底层操作使用一种强制转换语法:
在通用上下文中,编译器无法知道哪些是正确的,并且它们都生成不同的MSIL,因此它会挽救.
通过编写return (TCastTo)(object)i;
,强制编译器执行向上转换object
,然后向下转换为TCastTo
.编译器将生成代码,但如果这不是转换相关类型的正确方法,则会出现运行时错误.
代码示例:
public static class DefaultConverter<TInput, TOutput>
{
private static Converter<TInput, TOutput> cached;
static DefaultConverter()
{
ParameterExpression p = Expression.Parameter(typeof(TSource));
cached = Expression.Lambda<Converter<TSource, TCastTo>(Expression.Convert(p, typeof(TCastTo), p).Compile();
}
public static Converter<TInput, TOutput> Instance { return cached; }
}
public static class DefaultConverter<TOutput>
{
public static TOutput ConvertBen<TInput>(TInput from) { return DefaultConverter<TInput, TOutput>.Instance.Invoke(from); }
public static TOutput ConvertEric(dynamic from) { return from; }
}
Run Code Online (Sandbox Code Playgroud)
埃里克的方式肯定会更短,但我认为我应该更快.