1

私は共分散と反分散について読んでいます-ウィキペディアは次のことについて話しています:

人を表すクラスがあるとします。人は医者に診てもらうことができるので、このクラスにはメソッド virtual void Person::see(Doctor d) があるかもしれません。ここで、Person クラスのサブクラスである Child を作成するとします。つまり、子は人です。次に、Doctor、Pediatrician のサブクラスを作成したいと思うかもしれません。子供が小児科医だけを受診する場合は、型システムでそれを強制したいと考えています。ただし、単純な実装は失敗します。Child は Person であるため、Child::see(d) は小児科医だけでなく、任意の医師を対象とする必要があります。

これが「単純な実装」です。

public interface IDoctor
{
}

public interface IPerson
{
    void VisitDoctor(IDoctor doctor);
}

public class Adult : IPerson
{
    public void VisitDoctor(IDoctor doctor)
    {
        Console.WriteLine("Adult saw doctor of type: {0}", doctor.GetType().Name);
    }
}

public class Child : IPerson
{
    public void VisitDoctor(IDoctor doctor)
    {
        Console.WriteLine("Child saw doctor of type: {0}", doctor.GetType().Name);
    }
}

public class AdultDoctor : IDoctor
{
}

public class ChildDoctor : IDoctor
{
}

これらのテスト:

[Test]
public void AdultSeesDoctor()
{
    var adult = new Adult();
    adult.VisitDoctor(new AdultDoctor());
    adult.VisitDoctor(new ChildDoctor());  // <-- Would like this to fail
}

[Test]
public void ChildSeesDoctor()
{
    var child = new Child();
    child.VisitDoctor(new AdultDoctor());  // <-- Would like this to fail
    child.VisitDoctor(new ChildDoctor());
}

出力:

成人の医師の種類: AdultDoctor

成人の医師の種類: ChildDoctor

子供の医者のタイプ: AdultDoctor

子供はタイプの医師を見ました: ChildDoctor

これで、大人が子供の医者を訪ねようとした場合、または子供が大人の医者を訪ねようとした場合 ( をスローSystem.InvalidCastException)、実行時エラーをスローする次のコードを実装できます。

public interface IVisitDoctors<T> where T : IDoctor
{
    void VisitDoctor(T doctor);
}

public class Child : IPerson
{
    private readonly ChildDoctorVisitor _cdv = new ChildDoctorVisitor();

    public void VisitDoctor(IDoctor doctor)
    {
        _cdv.VisitDoctor((ChildDoctor)doctor);
    }
}

public class Adult : IPerson
{
    private readonly AdultDoctorVisitor _adv = new AdultDoctorVisitor();

    public void VisitDoctor(IDoctor doctor)
    {
        _adv.VisitDoctor((AdultDoctor)doctor);
    }
}

Adultのクラスがタイプ の医師のみを訪問するAdultDoctorように強制できますか?タイプの医師が訪問された場合にコンパイル時エラーがスローされますChildDoctor(クラス の場合はその逆Child)。

4

2 に答える 2

1

これには共分散または反分散は必要ありません。

public interface IDoctor<TPatient> where T : IPerson<TPatient>
{
}

public interface IPerson<T> where T : IPerson<T>
{
    void VisitDoctor(IDoctor<T> doctor);
}

public class Adult : IPerson<Adult>
{
    void VisitDoctor(IDoctor<Adult> doctor) {  }
}

public class AdultDoctor : IDoctor<Adult>
{
}

現在、以下はコンパイルに失敗します:

Adult a = new Adult();
a.VisitDoctor(new ChildDoctor());

これは次のようになります。

Adult a = new Adult();
a.VisitDoctor(new AdultDoctor());

これは、奇妙に繰り返されるテンプレート パターンと呼ばれます。この場合、Adultインターフェイス type を介して具体的な実装者の型 ( )を取得するために使用されますIPerson。これは、IPerson訪問できる医師のタイプを実装者と同じタイプに制限できることを意味します。

Java Enum クラスでも確認できます。このcompareToメソッドを使用すると、列挙型を比較できますが、同じ型の列挙型のみを比較できるようにするには、反復テンプレートが必要です。

ただし、これは非常に見苦しいので、次のようなデザインに変更することを検討してください。

public interface IDoctor<TPatient>
{
    void SeePatient(TPatient patient);
}

public interface IAppointments<T>
{
    void MakeAppointment(T patient, IDoctor<T> doctor);
}

IPersonしたがって、型パラメーターを持つ必要性を取り除くことができます。

于 2012-10-07T17:28:38.947 に答える
1

あなたが達成したいことを理解しています。しかし、このインターフェイスを引き続き使用する場合:

public interface IPerson
{
    void VisitDoctor(IDoctor doctor);
}

実装者に対するさらなる制限は、リスコフの原則を破ります。そして、それは後でコードに何らかの問題を引き起こす可能性が最も高い.

できることは、その制限をインターフェイス宣言に入れることです

public interface IPerson<TDoctor>
    where TDoctor : class, IDoctor...
{
    void VisitDoctor(TDoctor doctor);
}
于 2012-10-07T17:30:26.997 に答える