C# “是怎么回事?”;作为「;右侧操作数为泛型时转换的运算符? 我刚刚发布了一个ATO,但我不完全相信我的答案。有两件事我想知道,考虑一下这个代码: class Foo<T> { void SomeMethod() { string str = "foo"; Foo<T> f = str as Foo<T>; } }
如果C# “是怎么回事?”;作为「;右侧操作数为泛型时转换的运算符? 我刚刚发布了一个ATO,但我不完全相信我的答案。有两件事我想知道,考虑一下这个代码: class Foo<T> { void SomeMethod() { string str = "foo"; Foo<T> f = str as Foo<T>; } },c#,.net,dynamic,casting,as-operator,C#,.net,Dynamic,Casting,As Operator,如果E的编译时类型为dynamic,则与cast操作符不同,as操作符不受动态绑定(§7.2.2)。因此,这种情况下的扩展是: E is T ? (T)(object)(E) : (T)null 因为,这是无效的,因为(Foo)str str is Foo<T> ? (Foo<T>)str : (Foo<T>)null; str是Foo吗?(Foo)str:(Foo)null; 我想应该翻译成: str is Foo<T> ? (Foo<
E
的编译时类型为dynamic
,则与cast操作符不同,as操作符不受动态绑定(§7.2.2)。因此,这种情况下的扩展是:
E is T ? (T)(object)(E) : (T)null
因为,这是无效的,因为(Foo)str
str is Foo<T> ? (Foo<T>)str : (Foo<T>)null;
str是Foo吗?(Foo)str:(Foo)null;
我想应该翻译成:
str is Foo<T> ? (Foo<T>)(object)str : (Foo<T>)null;
str是Foo吗?(Foo)(object)str:(Foo)null;
但是规范说,只有当E
的类型是动态的时候才会发生这种情况
因此,我的问题是:
编译器是否将此表达式转换为通常无效的代码
当E
的类型是动态的时,为什么首先将E
强制转换为对象
,然后将T
强制转换为(T)E
编译器是否将此表达式转换为正常运行的代码
无效
在看了大约一个小时的规范之后,我开始说服自己,这只是规范中忽略的一个边缘案例。请注意,这只是C语言编写者使用is
运算符的语义将表示为运算符的一种方式
编译器实际上不会将as
运算符转换为具有is
的三元运算符。它将发出对isinst
的IL调用,用于as
和is
:
IL_0000: nop
IL_0001: ldstr "foo"
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: isinst class ConsoleApplication2.Foo`1<!T>
IL_000d: stloc.1
IL_000e: ret
需要强制转换到对象
,以便在动态
对象中尽可能地使用as
是编译时操作,而动态
对象仅在运行时绑定
编译器实际上将动态
类型对象视为类型对象
,首先:
class Foo<T>
{
public void SomeMethod()
{
dynamic str = "foo";
Foo<T> f = str as Foo<T>;
}
}
.class private auto ansi beforefieldinit Foo`1<T>
extends [mscorlib]System.Object
{
// Methods
.method public hidebysig
instance void SomeMethod () cil managed
{
// Method begins at RVA 0x2050
// Code size 15 (0xf)
.maxstack 1
.locals init (
[0] object,
[1] class Foo`1<!T>
)
IL_0000: nop
IL_0001: ldstr "foo"
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: isinst class Foo`1<!T>
IL_000d: stloc.1
IL_000e: ret
} // end of method Foo`1::SomeMethod
}
编辑:
在与托管语言团队的弗拉基米尔·雷舍特尼科夫(Vladimir Reshetnikov)交谈后,他解释了从“as操作符”到“cast操作符”的表示语义实际上试图说服什么:
我同意,规范中也有一些不精确的语言。它说,如果涉及开放类型,“as”运算符总是适用的,但随后用类型转换来描述其求值,这在某些情况下可能无效。应该说,扩展中的强制转换并不表示普通的C#cast运算符,而只是表示“as”运算符中允许的转换。我将注意修复它。谢谢
我不能100%确定编译器的操作,但在没有任何动态的情况下,编译器知道E是否为T
,因此可能不会实际发出给定的代码,而是执行适当的部分。在它是动态的情况下,我假设它与出于某种原因强迫它被装箱有关,但我对动态表达式的行为了解不够,除了猜测之外。。。
class Foo<T>
{
public void SomeMethod()
{
dynamic str = "foo";
Foo<T> f = str as Foo<T>;
}
}
.class private auto ansi beforefieldinit Foo`1<T>
extends [mscorlib]System.Object
{
// Methods
.method public hidebysig
instance void SomeMethod () cil managed
{
// Method begins at RVA 0x2050
// Code size 15 (0xf)
.maxstack 1
.locals init (
[0] object,
[1] class Foo`1<!T>
)
IL_0000: nop
IL_0001: ldstr "foo"
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: isinst class Foo`1<!T>
IL_000d: stloc.1
IL_000e: ret
} // end of method Foo`1::SomeMethod
}