为什么这是编译时错误?
public TCastTo CastMe<TSource, TCastTo>(TSource i)
{
return (TCastTo)i;
}
错误:
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);
我搜索了SO和互联网以获得答案,并找到了类似通用相关铸造问题的大量解释,但我找不到关于这个特殊简单案例的任何内容。
答案 0 :(得分:23)
为什么这是编译时错误?
问题在于,每种可能的值类型组合都有不同的规则,这些规则适用于强制转换的含义。将64位双精度转换为16位int与将十进制转换为浮点数完全不同,依此类推。可能性的数量是巨大的。所以想像编译器一样。 编译器应为您的程序生成哪些代码?
编译器必须生成在运行时再次启动编译器的代码,对类型进行全新分析,并动态发出相应的代码。
这看起来似乎可能比你预期的泛型更多的工作和更少的性能,所以我们简单地禁止它。如果您真正想要的是编译器再次启动并对类型进行分析,请在C#4中使用“dynamic”;这就是它的作用。
为什么这是运行时错误?
同样的道理。
由于与上述相同的原因,盒装的int只能解包为int(或int?);如果CLR尝试从盒装值类型到每个其他可能的值类型进行所有可能的转换,那么基本上它必须在运行时再次运行编译器。那会非常慢。
那么为什么引用类型不是错误呢?
因为每个引用类型转换与每个其他引用类型转换相同:您询问对象以查看它是从所需类型派生还是与所需类型相同。如果不是,则抛出异常(如果进行强制转换)或结果为null / false(如果使用“as / is”运算符)。规则对于引用类型是一致的,它们不是值类型。请记住引用类型知道自己的类型。价值类型没有;对于值类型,执行存储的变量是唯一知道适用于这些位的类型语义的东西。值类型包含其值,不包含其他信息。引用类型包含它们的值以及大量额外数据。
有关详细信息,请参阅我关于此主题的文章:
http://ericlippert.com/2009/03/03/representation-and-identity/
答案 1 :(得分:7)
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; }
}
Eric的方式肯定会更短,但我认为我应该更快。
答案 2 :(得分:2)
导致编译错误,因为无法将TSource隐式强制转换为TCastTo。这两种类型可以在其继承树上共享一个分支,但不能保证。如果您只想调用共享祖先的类型,则应修改CastMe()签名以使用祖先类型而不是泛型。
运行时错误示例通过首先将TSource i强制转换为对象来避免第一个示例中的错误,这是C#中所有对象派生自的对象。虽然编译器没有抱怨(因为对象 - >从它派生的东西,可能是有效的),如果强制转换无效,则抛出via(Type)变量语法的行为将抛出。 (与编译器在示例1中阻止发生的问题相同)。
另一种解决方案,它与你正在寻找的东西类似...
public static T2 CastTo<T, T2>(T input, Func<T, T2> convert)
{
return convert(input);
}
你会这样称呼它。
int a = 314;
long b = CastTo(a, i=>(long)i);
希望这有帮助。