2

C# チームが非ジェネリックの共変/反変をサポートしないことにしたのはなぜでしょうか。チームメンバーが答えるとは思わないので、質問はかなり主観的ですが、誰かが私 (およびバーバラ・リスコフ) に欠けている洞察を持っているかもしれません。

このサンプル インターフェイスを見てみましょう。

public interface ITest
{
    object Property
    {
        get;
    }
}

次の実装は失敗しますが、完全に安全です (インターフェイスに違反することなく、より具体的な型をいつでも返すことができます - C# ではなく、少なくとも理論的には)。

public class Test : ITest
{
    public string Property
    {
        get;
    }
}

インターフェイスにセッターが含まれている場合、コードは当然安全ではありませんが、ジェネリックの場合と同様に、 out/in を使用して安全性を宣言することで指摘できるため、実装全体を制限する理由にはなりません。

4

3 に答える 3

2

CLR は共変の戻り値の型をサポートしていませんが、.NET 2.0 以降ではデリゲート/インターフェイスの一般的な分散がサポートされています。

つまり、実際には C# チームではなく、CLR チームの責任です。

CLR が通常の分散をサポートしない理由については、おそらく必要な量の知覚される利点がなければ、複雑さが増すこと以外はわかりません。

編集: vtable の「スロット」について話している CLI 仕様のセクション 8.10.4 から、戻り値の型の共分散に関するポイントに対抗するには:

「既存のスロットを期待する」とマークされた新しいメンバーごとに、種類 (つまり、フィールドまたはメソッド)、名前、および署名が完全に一致するかどうかを確認し、見つかった場合はそのスロットを使用し、見つからない場合は新しいスロットを割り当てます。

パーティション II のセクション 9.9 から:

具体的には、メンバーが基本クラスまたはインターフェイスのメンバーを非表示にするか (静的メンバーまたはインスタンス メンバーの場合)、またはオーバーライドするか (仮想メソッドの場合) を判断するには、各ジェネリック パラメーターをそのジェネリック引数に置き換え、結果のメンバー シグネチャを比較します。

差異を許容する方法で比較が行われていることを示すものはありません。

CLR分散を許容していると思われる場合は、上記の証拠を考慮して、適切な IL でそれを証明するのはあなた次第だと思います。

編集: IL で試したところ、動作しません。このコードをコンパイルします。

using System;

public class Base
{
    public virtual object Foo()
    {
        Console.WriteLine("Base.Foo");
        return null;
    }
}

public class Derived : Base
{
    public override object Foo()
    {
        Console.WriteLine("Derived.Foo");
        return null;
    }
}

class Test
{
    static void Main()
    {
        Base b = new Derived();
        b.Foo();
    }
}

出力で実行します:

Derived.Foo

それを分解します:

ildasm Test.exe /out:Test.il

Derived.Foo「オブジェクト」ではなく「文字列」の戻り型を持つように編集します。

.method public hidebysig virtual instance string Foo() cil managed

再構築:

ilasm /OUTPUT:Test.exe Test.il

再実行すると、次の出力が得られます。

Base.Foo

つまり、CLR に関する限り、Derived.Foo は Base.Foo をオーバーライドしなくなりました。

于 2009-08-09T11:23:33.113 に答える
1

CLR はメソッド オーバーライドのバリアンスをサポートしていませんが、インターフェイスの実装には回避策があります。

public class Test : ITest
{
    public string Property
    {
        get;
    }

    object ITest.Property
    {
        get
        {
            return Property;
        }
    }
}

これにより、共変オーバーライドと同じ効果が得られますが、インターフェイスと直接実装にのみ使用できます

于 2009-08-09T17:30:12.183 に答える
1
  1. メソッドの戻り値は常に「out」であり、常に代入式の右側にあります。
  2. CLR アセンブリ形式には、インターフェイス メソッドにインスタンス メソッドを割り当てるメタデータがありますが、このメソッドの推論は入力パラメーターにのみ依存するため、サポートする新しい形式を作成する必要がある場合があり、あいまいになるだけではありません。
  3. インスタンス メソッドとインターフェイス シグネチャの間の新しいメソッド マッピング アルゴリズムは、複雑で CPU を集中的に使用する可能性があります。また、インターフェイス メソッドのメソッド シグネチャはコンパイル時に解決されると思います。実行時にコストがかかりすぎる可能性があるためです。
  4. 以下で説明するように、メソッドの推論/解決が問題になる可能性があります。

異なる戻り値の型のみを使用して動的メソッド解決を許可する次のサンプルを検討してください (これは絶対に間違っています)。

public class Test{
     public int DoMethod(){ return 2; }
     public string DoMethod() { return "Name"; }
} 
Test t;
int n = t.DoMethod();  // 1st method
string txt = t.DoMethod(); // 2nd method
object x = t.DoMethod(); // DOOMED ... which one??
于 2009-08-09T12:34:36.127 に答える