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

时间:2015-01-26 13:49:04

标签: c# .net dynamic casting as-operator

我刚刚向answer发布了this question,但我并不完全相信我的回答。我想知道有两件事情,请考虑以下代码:

class Foo<T>
{ 
    void SomeMethod()
    {
        string str = "foo";
        Foo<T> f = str as Foo<T>;
    }
}

根据C# Specification 5.0as operator有两种不同的转换方式。

  

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

相同的结果
E is T ? (T)(E) : (T)null
     

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

E is T ? (T)(object)(E) : (T)null

因为(Foo<T>)str

,这是无效的
str is Foo<T> ? (Foo<T>)str : (Foo<T>)null;

我认为它应该被翻译为:

str is Foo<T> ? (Foo<T>)(object)str : (Foo<T>)null;

但规范说这只会在E的类型为dynamic时才会发生。

所以我的问题是:

  1. 编译器是否将此表达式转换为通常无效的代码?
  2. 如果E的类型是动态的,为什么首先它将E投放到object然后T,而(T)E完全有效?

1 个答案:

答案 0 :(得分:12)

  

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

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

编译器实际上不会将as运算符转换为具有is的三元运算符。对于isinstas,它会向is发出IL调用:

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

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

  

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

这在说明书的细则中有描述:

  

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

E is T ? (T)(object)(E) : (T)null

需要使用object转换为as对象dynamicas编译时操作,而dynamic个对象仅在运行时绑定。

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

class Foo<T> 
{
    public void SomeMethod() 
    {
        dynamic str = "foo";
        Foo<T> f = str as Foo<T>;
    }
}
实际上,

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
}

编辑:

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

  

我同意,规范中也有一些不精确的语言。它表示'as'运算符始终适用于涉及的开放类型,但随后描述了其在演员表方面的评估,在某些情况下可能无效。它应该说扩展中的强制转换不代表普通的C#强制转换运算符,而只是代表'as'运算符中允许的转换。我会记下来修复它。谢谢!