1. ホーム
  2. c#

C# オーバーライドされたメソッドのオプションパラメータ

2023-08-29 08:47:13

質問

.NET Frameworkで、メソッドをオーバーライドするときに、オプションのパラメータに問題があるようです。下記のコードの出力は "bbb" "aaa" . しかし、私が期待している出力は "bbb" "bbb" .これに対する解決策はありますか。私はそれがメソッドのオーバーロードで解決することができることを知っていますが、この理由を疑問に思っています。また、コードはMonoでうまく動作します。

class Program
{
    class AAA
    {
        public virtual void MyMethod(string s = "aaa")
        {
            Console.WriteLine(s);
        }

        public virtual void MyMethod2()
        {
            MyMethod();
        }
    }

    class BBB : AAA
    {
        public override void MyMethod(string s = "bbb")
        {
            base.MyMethod(s);
        }

        public override void MyMethod2()
        {
            MyMethod();
        }
    }

    static void Main(string[] args)
    {
        BBB asd = new BBB();
        asd.MyMethod();
        asd.MyMethod2();
    }
}

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

ここで注意すべき点は、オーバーライドされたバージョンが毎回呼び出されることです。オーバーライドを変更します。

public override void MyMethod(string s = "bbb")
{
  Console.Write("derived: ");
  base.MyMethod(s);
}

と出力されます。

derived: bbb
derived: aaa

クラス内のメソッドは、次のうち1つまたは2つを行うことができます。

  1. 他のコードが呼び出すためのインターフェイスを定義する。
  2. 呼び出されたときに実行するための実装を定義します。

抽象メソッドは前者のみを行うので、両方を行うことはできないかもしれません。

内では BBB 呼び出し MyMethod() はメソッドを呼び出します。 定義された AAA .

にオーバーライドがあるため BBB に実装されているので、そのメソッドを呼び出すと BBB が呼び出されることになります。

では、定義された AAA の定義は呼び出し側のコードに2つのことを知らせます (まあ、ここでは重要でないいくつかの他のこともですが)。

  1. シグネチャ void MyMethod(string) .
  2. (サポートしている言語では)単一パラメータのデフォルト値は "aaa" であり、したがって、以下の形式のコードをコンパイルするときは MyMethod() にマッチするメソッドがない場合は MyMethod() にマッチするメソッドが見つからない場合は、 `MyMethod("aaa")' の呼び出しに置き換えることができます。

での呼び出しがそうなんですね。 BBB の呼び出しがそうさせるのです。コンパイラは MyMethod() への呼び出しを見ますが、メソッド MyMethod() は見つかりませんでしたが、メソッドが見つかりました。 MyMethod(string) . また、このメソッドが定義されている場所にはデフォルト値として "aaa" があることもわかりました。 MyMethod("aaa") .

内部から BBB , AAA は、その場所とされる AAA のメソッドが定義される場所とみなされます。 BBB でオーバーライドされたとしても ができます。 を上書きすることができます。

ランタイムに MyMethod(string) は引数 "aaa" で呼び出されます。オーバーライドされたフォームがあるため、そのフォームが呼び出されますが、その値は実行時の実装とは関係なく、コンパイル時の定義であるため、"bbb" で呼び出されることはありません。

追加する this. を追加すると、どの定義が調べられるかが変わるので、呼び出しで使用される引数も変わってきます。

編集:なぜこれがより直感的に思えるのか。

個人的には、何が直感的かについて話しているので、それは個人的なものでしかありませんが、次の理由から、私はこの方が直感的であると思います。

私がコーディングしていた場合 BBB を呼び出してもオーバーライドしても MyMethod(string) を実行することだと考えています。 AAA stuff" - それは BBB のテイクオン。 AAA をすることです。 AAA をやっているのと同じです。したがって、呼び出すにせよオーバーライドするにせよ、私はそれが AAA を定義したのは MyMethod(string) .

を使用するコードを呼び出す場合 BBB を使用するコードを呼び出しているとしたら、私は次のように考えるでしょう。 BBB のもの"です。で元々定義されていたものがどれなのか、あまり意識していないかもしれません。 AAA で定義されたものをあまり意識しないかもしれませんし、これは単なる実装の詳細だと思うかもしれません(もし、私がまた AAA インターフェースも使っていなければ)。

コンパイラの動作は私の直感と一致します。そのため、最初に質問を読んだとき、Monoにバグがあるように思えたのです。考慮した結果、私は、どちらかが他よりも指定された動作をよりよく満たすということは見当たりません。

そのことについては、個人的なレベルにとどまっていますが、私は抽象的、仮想的、またはオーバーライドされたメソッドでオプションのパラメーターを決して使用せず、そうする他の誰かのものをオーバーライドする場合、私は彼らのものと一致させます。