6

好奇心から、コンパイラが制約のないジェネリック型をtypeof(object)とは異なる方法で処理するのはなぜですか?

class Bar { }

class Foo
{
    void foo(object thing)
    {
        ((Bar)thing).ToString();
    }
}

class Foo<T>
{
    void foo(T thing)
    {
        ((Bar)thing).ToString();
    }
}

上記では、「Tthing」をBarにキャストすると、コンパイラエラーが発生します。ただし、「オブジェクトのもの」をBarにキャストすることは、もちろんコンパイラーが私にできることです。

見えないのはその理由です。結局のところ、.netオブジェクトはキャッチオールであり、実行時型はボックス化された値または任意の型のオブジェクトである可能性があります。したがって、コンパイラが2つのケースを区別する論理的な理由がわかりません。私ができる最善のことは、「プログラマーは、コンパイラーがジェネリック型で型チェックを行うことを期待しますが、オブジェクトでは行わないことを期待する」のようなものです。:)それですべてですか?

ところで、私は、Fooの場合でも、書くだけでキャストを完了できることを認識しています。

((Bar)(object)thing).ToString();

コンパイラがこれを行う理由を理解したいだけです...

4

2 に答える 2

4

ここでの重要性はobject. 最初の例がそれ以外の場合objectは、同じように動作します。基本的に、あなたが今ここで言っていることは:

(Bar)thing

Tつまり、「aを a に変換するBar」; これは、一般的なケースでは合法とは言えません。追加objectすると、次のようになります。

(Bar)(object)thing

Tこれは「aを... に変換する」であり、すべてのマネージド型のルートであるobjectため、常に有効です。objectそして、これはボックスを呼び出すかもしれないことに注意してください - "...そしてobject、" - としてBar再びキャストします。コンパイル時には常に有効であり、実行時に型チェック ("unbox-any") が行われます。

例: ... だTとします。DateTime

DateTime thing = ...
Bar bar = (Bar)(object)thing;

完全に有効です。実行時に失敗することは確かですが、これは心に留めておく必要があるシナリオです。

于 2013-02-15T14:14:11.030 に答える
4

それは、ジェネリックを作成するセマンティクスと目的に帰着します。一般的な型 T を持っている場合、コンパイラはそれを他のオブジェクトに直接キャストすることを許可しません。T の目的は、プログラマーに T の実際の型を指定させることであるため、これは理にかなっています。「オブジェクト」ではなく、特定のタイプのオブジェクトになります。コンパイル時に、コンパイラは T に何が入るかを知ることができないため、キャストできません。

オブジェクトからのキャストは、匿名オブジェクトとして機能します。使用法で定義される KNOWN オブジェクト タイプとは対照的です。

これは、「where」句で拡張できます。たとえば、T が IBar 型でなければならないことを指定できます。

interface IBar { }

class Bar : IBar { }

class Foo<T>
    where T : IBar
{
    void foo(T thing)
    {
        ((IBar)thing).ToString();
    }
}

継承は where 句でも機能します。

class Bar { }

class Foo<T>
    where T : Bar
{
    void foo(T thing)
    {
        // now you don't need to cast at all as the compiler knows
        // exactly what type T is (at a parent level at least)
        thing.ToString();
    }
}
于 2013-02-15T14:19:32.710 に答える