C# 铸造vs';as';接线员重访
我知道已经有几篇文章讨论了cast和C# 铸造vs';as';接线员重访,c#,clr,C#,Clr,我知道已经有几篇文章讨论了cast和as操作符之间的区别。它们大多都重申了相同的事实: 如果强制转换失败,as操作符将不会抛出,而是返回null 因此,as运算符仅适用于引用类型 as运算符将不使用用户定义的转换运算符 然后,答案往往会无休止地争论如何使用或不使用其中一种,以及每种方法的优缺点,甚至是它们的性能(我一点也不感兴趣) 但这里还有更多的东西在起作用。考虑: static void MyGenericMethod<T>(T foo) { var myBar1 =
as
操作符之间的区别。它们大多都重申了相同的事实:
- 如果强制转换失败,
操作符将不会抛出,而是返回as
null
- 因此,
运算符仅适用于引用类型as
运算符将不使用用户定义的转换运算符as
static void MyGenericMethod<T>(T foo)
{
var myBar1 = foo as Bar; // compiles
var myBar2 = (Bar)foo; // does not compile ('Cannot cast expression of
// type 'T' to type 'Bar')
}
为什么
as
操作符编译而cast不编译 编译器不知道如何生成适用于所有情况的代码
以这两个电话为例:
MyGenericMethod(new Foo1());
MyGenericMethod(new Foo2());
现在假设Foo1
包含一个cast操作符,可以将其转换为Bar
实例,而Foo2
则从Bar
下降。显然,所涉及的代码在很大程度上取决于您传入的实际T
在您的特定情况下,您说该类型已经是Bar
类型,因此显然编译器可以只进行引用转换,因为它知道这是安全的,不需要进行任何转换
<>现在,<代码> AS >代码>转换更为“探索性”,它不仅不考虑用户的转换,它明确地允许了该语句是无意义的,所以编译器允许该幻灯片。 这是类型安全的问题。 任何
T
都不能转换为Bar
,但任何T
都可以被“视”为aBar
,因为即使没有从T
转换为Bar
,行为也有很好的定义
我们是否应该怀疑演员是
全部或部分在编译时解决
时间和as操作员是否
在问题的开头,您自己给出了答案:“as运算符将不使用用户定义的转换运算符”-与此同时,cast会使用,这意味着它需要在编译时找到这些运算符(或它们不存在)
请注意,就编译器而言
关于这一点,目前尚不清楚
服务器之间的连接(在上未知)
编译时)键入T和Bar
T类型未知的事实意味着编译器无法知道它与Bar之间是否没有连接
请注意,(Bar)(object)foo
确实有效,因为任何类型都不能有到object的转换运算符[因为它是所有事物的基类],而且从object到Bar的转换都不需要处理转换运算符。第一个编译只是因为as
关键字就是这样定义的。如果无法强制转换,它将返回null
。它是安全的,因为as
关键字本身不会引起任何运行时问题。您可能检查变量是否为空是另一回事
将视为一种TryCast方法。解决您的第一个问题:不仅仅是as
操作符忽略了用户定义的转换,尽管这是相关的。更相关的是cast操作符做了两件相互矛盾的事情。cast运算符表示:
我知道这个编译时类型Foo的表达式实际上是运行时类型Bar的对象。编译器,我现在告诉你这个事实,以便你能利用它。请生成代码,假设我是正确的;如果我不正确,那么您可以在运行时抛出异常
我知道这个编译时类型Foo的表达式实际上是运行时类型Foo。有一种标准方法可以将Foo的部分或所有实例转换为Bar的实例。编译器,请生成这样的转换,如果在运行时发现转换的值不可转换,则在运行时抛出异常
这些是相反的。巧妙的技巧,有一个做相反事情的操作员
相比之下,as
运算符只有第一个意义。as
只执行装箱、取消装箱和保留表示的转换。一个cast可以完成所有这些,再加上额外的表示形式更改转换。例如,将int转换为short会将表示形式从四字节整数更改为两字节整数
这就是为什么“原始”类型转换在不受约束的泛型上是不合法的;因为编译器没有足够的信息来确定它是哪种类型的强制转换:装箱、取消装箱、保留表示或更改表示。用户的期望是,强制转换泛型代码具有强制转换强类型代码的所有语义,而我们无法高效地生成该代码
考虑:
static void MyGenericMethod<T>(T foo)
{
var myBar1 = foo as Bar; // compiles
var myBar2 = (Bar)foo; // does not compile ('Cannot cast expression of
// type 'T' to type 'Bar')
}
void M<T, U>(T t, out U u)
{
u = (U)t;
}
void M(T,out U)
{
u=(u)t;
}
你认为这样行吗?我们生成哪些代码可以处理:
M<object, string>(...); // explicit reference conversion
M<string, object>(...); // implicit reference conversion
M<int, short>(...); // explicit numeric conversion
M<short, int>(...); // implicit numeric conversion
M<int, object>(...); // boxing conversion
M<object, int>(...); // unboxing conversion
M<decimal?, int?>(...); // lifted conversion calling runtime helper method
// and so on; I could give you literally hundreds of different cases.
M(…);//显式引用转换
M(…);//隐式引用转换
M(…);//显式数字转换
M(…);//隐式数字转换
M(…);//拳击转换
M(…);//拆箱转换
M(…);//调用运行时帮助程序方法的提升转换
//等等;我可以给你上百个不同的箱子。
基本上,我们必须为再次启动编译器的测试发出代码,对表达式进行完整分析,然后发出新代码。我们在C#4中实现了这个特性;它被称为“动态”,如果这是你想要的行为,你可以随意使用它
对于as
,我们没有这些问题,因为as
只做三件事。它进行装箱转换、取消装箱转换和类型测试,我们可以轻松生成完成这三项工作的代码。在我看来,它就像fooasbar