43

私の知る限り、C#2.0で次のことを行うことはできません

public class Father
{
    public virtual Father SomePropertyName
    {
        get
        {
            return this;
        }
    }
}

public class Child : Father
{
    public override Child SomePropertyName
    {
        get
        {
            return this;
        }
    }
}

派生クラスでプロパティを「新規」として作成することで問題を回避しますが、もちろんそれはポリモーフィックではありません。

public new Child SomePropertyName

2.0で何か解決策はありますか? この問題に対処する 3.5 の機能はどうですか?

4

9 に答える 9

44

再宣言 (新規) はできますが、再宣言とオーバーライドを同時に (同じ名前で) 行うことはできません。1 つのオプションは、保護されたメソッドを使用して詳細を非表示にすることです。これにより、ポリモーフィズムと非表示の両方が同時に可能になります。

public class Father
{
    public Father SomePropertyName
    {
        get {
            return SomePropertyImpl();
        }
    }
    protected virtual Father SomePropertyImpl()
    {
        // base-class version
    }
}

public class Child : Father
{
    public new Child SomePropertyName
    {
        get
        { // since we know our local SomePropertyImpl actually returns a Child
            return (Child)SomePropertyImpl();
        }
    }
    protected override Father SomePropertyImpl()
    {
        // do something different, might return a Child
        // but typed as Father for the return
    }
}
于 2008-10-01T11:08:06.760 に答える
29

これは、タイプ セーフの問題により、どの .NET 言語でも可能ではありません。タイプ セーフな言語では、戻り値の共変性とパラメーターの反変性を提供する必要があります。次のコードを使用します。

class B {
    S Get();
    Set(S);
}
class D : B {
    T Get();
    Set(T);
}

Getメソッドの場合、共分散とは、またはから派生した型のTいずれかでなければならないことを意味します。それ以外の場合、変数 typedに格納された型のオブジェクトへの参照がある場合、呼び出したときに、型システムを壊して、バックとして表現可能なオブジェクトを取得できません。SSDBB.Get()S

Setメソッドの場合、反変性とは、または派生した型であるT必要があることを意味します。それ以外の場合、変数 typedに格納されている型のオブジェクトへの参照がある場合、 を呼び出したときに、was は type であり typeではなく、予期しない型のオブジェクトを取得します。SSDBB.Set(X)XSTD::Set(T)

C# では、getter/setter のペアが 1 つしかない場合でも、プロパティをオーバーロードするときに型の変更を許可しないという意識的な決定がありました。 1 つはゲッターを備えていますが、1 つはゲッターとセッターの両方を備えているわけではありません?なぜですか?!?" -- 匿名の代替宇宙初心者)。

于 2008-10-01T11:59:46.323 に答える
13

いいえ。ただし、2 以降ではジェネリックを使用できます。

public class MyClass<T> where T: Person
{
    public virtual T SomePropertyName
    {
        get
        {
            return  ...;
        }
    }
}

次に、Father と Child は同じクラスのジェネリック バージョンです

于 2008-10-01T11:09:21.577 に答える
7

ウィキペディアから:

C# プログラミング言語では、言語のバージョン 2.0 で、デリゲートの戻り値の型の共変性とパラメーターの反変性の両方のサポートが追加されました。メソッドのオーバーライドでは、共分散も反分散もサポートされていません。

ただし、プロパティの共分散については明示的に何も述べていません。

于 2008-10-01T11:08:21.500 に答える
2

これは私が(これまでに)来ることができた最も近いものです:

public sealed class JustFather : Father<JustFather> {}

public class Father<T> where T : Father<T>
{ 
    public virtual T SomePropertyName
    { get { return (T) this; }
    }
}

public class Child : Father<Child>
{ 
    public override Child SomePropertyName
    { get { return  this; }
    }
}

クラスがないと、他の派生型でない限り、をJustFatherインスタンス化できませんでした。Father<T>

于 2008-10-09T23:55:35.603 に答える
2

父と子に共通のインターフェースを作成し、そのインターフェースの型を返すことができます。

于 2008-10-01T11:18:15.377 に答える
1

いいえ。C# はこの考え方をサポートしていません (「戻り型の共分散」と呼ばれます)。ただし、これを行うことができます:

public class FatherProp
{
}

public class ChildProp: FatherProp
{
}


public class Father
{
    public virtual FatherProp SomePropertyName
    {
        get
        {
            return new FatherProp();
        }
    }
}


public class Child : Father
{
    public override FatherProp SomePropertyName
    {
        get
        {
            // override to return a derived type instead
            return new ChildProp();
        }
    }
}

つまり、基本クラスで定義されたコントラクトを使用しますが、派生型を返します。この点を明確にするために、より詳細なサンプルを作成しました。もう一度「this」を返しても、何も変わりません。

返されたオブジェクトの実際の型をテストすることは可能ですが (面倒です) (つまり、「someObject が ChildProp の場合」)、その型に対して正しいことを行う仮想メソッドを呼び出す方が適切です。

基本クラスの仮想メソッド (この場合は仮想プロパティ) は実装を持つだけでなく、コントラクトも定義します。このコントラクトを満たす場合、子クラスは SomePropertyName の別の実装を提供できます (つまり、 SomePropertyName は型 " のオブジェクトを返します)。ファーザープロップ」)。「FatherProp」から派生したタイプ「ChildProp」のオブジェクトを返すことは、この契約を満たしています。ただし、「子」のコントラクトを変更することはできません。このコントラクトは、「父」の子孫のすべてのクラスに適用されます。

一歩下がって、より広範な設計を検討すると、C# ツールキットには、ジェネリックまたはインターフェイスなど、代わりに考えた方がよい言語構造が他にもあります。

于 2008-10-01T11:06:28.167 に答える
1

いいえ。C# はこの考え方をサポートしていません (「戻り型の共分散」と呼ばれます)。

ウィキペディアから:

C# プログラミング言語では、言語のバージョン 2.0 で、デリゲートの戻り値の型の共変性とパラメーターの反変性の両方のサポートが追加されました。メソッドのオーバーライドでは、共分散も反分散もサポートされていません。

再宣言 (新規) はできますが、再宣言とオーバーライドを同時に (同じ名前で) 行うことはできません。1 つのオプションは、保護されたメソッドを使用して詳細を非表示にすることです。これにより、ポリモーフィズムと非表示の両方が同時に可能になります。

最善の解決策は、ジェネリックを使用することです。

public class MyClass<T> where T: Person
{
   public virtual T SomePropertyNameA
   {        
      get { return  ...; }    
   }
}//Then the Father and Child are generic versions of the same class
于 2008-10-01T11:56:11.853 に答える