17

この質問への回答を投稿したばかりですが、回答に完全には確信が持てません。疑問に思っていることが2つあります。次のコードを検討してください。

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

によるとC# Specification 5.0、 の変換には 2 つの異なる種類がありas 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するのはなぜですか?objectT(T)E
4

1 に答える 1

12

コンパイラは、この式を通常は無効なコードに変換していますか?

約 1 時間仕様を見つめた後、これは単に仕様で見落とされていた特殊なケースであると確信し始めています。asこれは、C# 言語のコンポーザが演算子のセマンティクスを使用して演算子を表現するための単なる方法であることに注意してくださいis

コンパイラは実際asには、演算子を . を使用して三項演算子に変換しませんisisinstとの両方に対して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

コンパイルされた DLL を見ると、asオペレーターは手付かずのままです。

E の型が動的な場合、(T)E が完全に有効であるのに、最初に E をオブジェクトにキャストし、次に T をキャストするのはなぜですか?

これは、仕様の詳細に記載されています。

E のコンパイル時の型が動的である場合、キャスト演算子とは異なり、 as 演算子は動的にバインドされません(§7.2.2)。したがって、この場合の展開は次のようになります。

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

オブジェクトでを使用できるようにするには、 toへのキャストobjectが必要です。はコンパイル時の操作ですが、オブジェクトは実行時にのみバインドされます。asdynamicasdynamic

コンパイラは、実際には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' 演算子で許可されている変換を表すだけあると言う必要があります。修正するのでメモしておきます。ありがとう!

于 2015-01-26T17:17:09.553 に答える