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



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

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

如果E的编译时类型是dynamic,则与强制转换运算符不同,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)E完全有效时T

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

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

编译器实际上并没有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

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

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

这在规范的细则中进行了描述:

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

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

需要对object进行投射,以便对dynamic对象使用asas编译时操作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
}

编辑:

在与托管语言团队的 Vladimir Reshetnikov 交谈后,他解释了从"as operator"到"cast operator"的表示语义实际上试图表达的内容:

我同意,规范中也有一些不精确的语言。它说,如果涉及开放类型,则"as"运算符始终适用,但随后根据强制转换来描述其评估,这在某些情况下可能无效。应该说,扩展中的强制转换不表示正常的 C# 强制转换运算符,而只是表示"as"运算符中允许的转换。我会记下来修复它。谢谢!

相关内容

  • 没有找到相关文章

最新更新