当右侧操作数是通用的时,如何翻译"as"运算符?

Sel*_*enç 17 .net c# casting dynamic as-operator

我刚刚发布了这个问题答案,但我并不完全相信我的答案.我想知道有两件事,请考虑以下代码:

class Foo<T>
{ 
    void SomeMethod()
    {
        string str = "foo";
        Foo<T> f = str as Foo<T>;
    }
}
Run Code Online (Sandbox Code Playgroud)

据说C# Specification 5.0,有两种不同的转换方式as operator.

如果编译时类型E不是dynamic,则操作E as T产生与之相同的结果

E is T ? (T)(E) : (T)null
Run Code Online (Sandbox Code Playgroud)

如果编译时类型Edynamic,则与as operator强制转换运算符不同,它不是动态绑定的(第7.2.2节).因此,在这种情况下的扩展是:

E is T ? (T)(object)(E) : (T)null
Run Code Online (Sandbox Code Playgroud)

因为,这是无效的 (Foo<T>)str

str is Foo<T> ? (Foo<T>)str : (Foo<T>)null;
Run Code Online (Sandbox Code Playgroud)

我认为它应该被翻译为:

str is Foo<T> ? (Foo<T>)(object)str : (Foo<T>)null;
Run Code Online (Sandbox Code Playgroud)

但是,规范说,当类型这只是发生Edynamic.

所以我的问题是:

  1. 编译器是否将此表达式转换为通常无效的代码?
  2. 当类型E是动态的,所以首先它转换Eobject随后T尽管(T)E是完全有效的?

Yuv*_*kov 12

编译器是否将此表达式转换为通常无效的代码?

在盯着规范大约一个小时之后,我开始说服自己这只是一个边缘案例,在规范中被忽略了.请注意,这仅仅是C#语言编写者用as运算符的语义表达运算符的一种方式is.

编译器实际上并没有as运算符转换为带有的三元运算符is.它将发出一个IL调用isinst,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
Run Code Online (Sandbox Code Playgroud)

查看已编译的DLL,as运算符保持不变.

当E的类型是动态的时候,为什么首先它将E转换为对象然后T,而(T)E完全有效?

这在规范的细则中有所描述:

如果E的编译时类型是动态的,与转换运算符不同,as运算符不是动态绑定的(第7.2.2节).因此,在这种情况下的扩展是:

E is T ? (T)(object)(E) : (T)null
Run Code Online (Sandbox Code Playgroud)

对演员object是需要做出使用的as可能的dynamic对象.as是一个编译时操作,而dynamic对象仅在运行时绑定.

编译器实际上将dynamic类型对象视为object开头的类型:

class Foo<T> 
{
    public void SomeMethod() 
    {
        dynamic str = "foo";
        Foo<T> f = str as Foo<T>;
    }
}
Run Code Online (Sandbox Code Playgroud)

str实际上被视为object开头:

.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
}
Run Code Online (Sandbox Code Playgroud)

编辑:

在与Managed Languages Team的Vladimir Reshetnikov交谈之后,他解释了从"as operator"到"cast operator"的表示语义实际上是什么意思:

我同意,规范中也有一些不精确的语言.它表示'as'运算符始终适用于涉及的开放类型,但随后描述了其在演员表方面的评估,在某些情况下可能无效.这应该说在扩张蒙上那并不代表正常的C#转换操作符,但只是表示允许在"为"经营者的转换.我会记下来解决它.谢谢!