1. ホーム
  2. c#

[解決済み] C#: 戻り値のオーバーライド

2023-05-10 16:36:33

質問

C# で戻り値の型をオーバーライドする方法はありますか?もしあればどのように、なければなぜ、そして推奨される方法は何ですか?

私のケースは、抽象的なベースクラスとその子孫を持つインターフェースを持っているということです。私はこれをしたいと思います(実際にはそうではありませんが、例として!)。

public interface Animal
{
   Poo Excrement { get; }
}

public class AnimalBase
{
   public virtual Poo Excrement { get { return new Poo(); } }
}

public class Dog
{
  // No override, just return normal poo like normal animal
}

public class Cat
{
  public override RadioactivePoo Excrement { get { return new RadioActivePoo(); } }
}

RadioactivePoo はもちろん Poo .

私がこれを望む理由は、"li" と "li "の間にある Cat オブジェクトを使う人が Excrement をキャストすることなく PooRadioactivePoo に変換し、例えば Cat の一部である可能性があります。 Animal の一部であり、ユーザーは必ずしも放射性うんちを意識したり気にしたりしないかもしれません。ご理解いただけたでしょうか...

私が見る限り、コンパイラは少なくともこれを許可していません。ですから、それは不可能だと思います。しかし、これに対する解決策として何をお勧めしますか?

どのように解決するのですか?

この問題に対する解決策はすでにたくさんありますが、私は既存の解決策で抱えていた問題を解決する方法を思いついたと思います。

私は以下の理由で既存のソリューションのいくつかに満足していませんでした。

  • Paolo Tedescoの最初の解答です。 Cat と Dog は共通のベースクラスを持っていません。
  • Paolo Tedescoの2番目の解決策です。 少し複雑で読みにくいです。
  • Daniel Daranas氏の解答です。 これは動作しますが、多くの不要なキャストとDebug.Assert()ステートメントでコードを混乱させるでしょう。
  • hjb417の解決策です。 この解決策では、ロジックを基底クラスで保持することができません。この例ではロジックはかなり些細なものですが(コンストラクタを呼び出す)、実際の例ではそうではないでしょう。

私の解決策

この解決策は、ジェネリックとメソッド隠蔽の両方を使用することで、上記で述べたすべての問題を克服できるはずです。

public class Poo { }
public class RadioactivePoo : Poo { }

interface IAnimal
{
    Poo Excrement { get; }
}

public class BaseAnimal<PooType> : IAnimal
    where PooType : Poo, new()
{
    Poo IAnimal.Excrement { get { return (Poo)this.Excrement; } }

    public PooType Excrement
    {
        get { return new PooType(); }
    }
}

public class Dog : BaseAnimal<Poo> { }
public class Cat : BaseAnimal<RadioactivePoo> { }

このソリューションでは、DogまたはCatで何かをオーバーライドする必要はありません。以下は使用例です。

Cat bruce = new Cat();
IAnimal bruceAsAnimal = bruce as IAnimal;
Console.WriteLine(bruce.Excrement.ToString());
Console.WriteLine(bruceAsAnimal.Excrement.ToString());

これは "RadioactivePoo" を2回出力し、多相性が崩れていないことを示します。

さらなる読み方

  • 明示的なインターフェイスの実装
  • 新しいモディファイア . この単純化されたソリューションでは使用しませんでしたが、より複雑なソリューションでは必要になるかもしれません。例えば、BaseAnimal のインターフェースを作成したい場合、 "PooType Excrement" のデクレレーションでそれを使用する必要があります。
  • out Generic Modifier (Covariance) (共分散) . このソリューションでも使いませんでしたが、もしreturnのようなことをしたかったら MyType<Poo> をIAnimalから取得して MyType<PooType> を返すのであれば、この2つの間でキャストを行えるようにするために使う必要があります。