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


Answers

あなたは電話で曖昧さを解消することができます:

this.MyMethod();

MyMethod2()

バグかどうかは難しいです。 しかし、それは矛盾して見える。 Resharperは、上書きのデフォルト値を変更しないように警告します;もしそれが役立つならば、もちろん、resharperはthis.あなたに伝えますthis. 冗長であり、あなたのためにそれを取り除くことを提案します...それは振る舞いを変えます - 従ってresharperも完璧ではありません。

それはコンパイラのバグと見なされるようですが、私はあなたに与えます。 私は本当に慎重に見なければならないだろう...あなたが彼を必要とするとき、エリックはどこですか?

編集:

ここでのポイントは言語仕様です。 7.5.3を見てみましょう:

たとえば、メソッド呼び出しの候補セットには、オーバーライド(§7.4)とマークされたメソッドは含まれず、派生クラス内のメソッドが適用可能であれば、基本クラスのメソッドは候補ではありません(§7.6.5.1)。

(そして確かに§7.4は、考慮からのoverride方法を明確に省略している)

ここにはいくつかの矛盾があります。派生クラスに適用可能なメソッドがある場合、 基本メソッドは使用されないと言いoverrideこれは派生メソッドにつながりoverrideが、同時にoverrideれたメソッドは考慮されていません。

しかし、7.5.1.1項は、

クラス内で定義された仮想メソッドおよびインデクサの場合、パラメータリストは、関数メンバの最も具体的な宣言またはオーバーライドから選択されます。受信者の静的型から開始し、その基本クラスを検索します。

§7.5.1.2では、呼び出し時に値がどのように評価されるかについて説明しています。

関数メンバ呼び出し(7.5.4)の実行時処理中に、引数リストの式または変数参照は、左から順に次のように評価されます。

...(スニップ)...

対応するオプションのパラメーターを持つ関数メンバーから引き数を省略すると、関数メンバー宣言のデフォルトの引き数が暗黙的に渡されます。 これらは常に一定であるため、評価は残りの引数の評価順序に影響を与えません。

これは、以前に§7.5.1.1で最も具体的な宣言または上書きから来たものとして定義されていた引数リストを見ていることを明示的に強調しています 。 これが§7.5.1.2で言及されている "メソッド宣言"であることは妥当と思われます。したがって、渡される値は、最も派生したものから静的な型に至る必要があります。

これは、cscにバグがあり、基本メソッドの宣言(§7.6)を参照するように制限されていない限り、 派生バージョン( "bbb bbb")を使用している必要があります( base. 、 8)。

Question

.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();
    }
}



これはあいまいさが原因である可能性があり、コンパイラはベース/スーパークラスを優先しています。 thisキーワードへの参照を追加してクラスBBBのコードを以下に変更すると、出力 'bbb bbb'が得られます。

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

    public override void MyMethod2()
    {
        this.MyMethod(); //added this keyword here
    }
}

それが意味することの1つは、 ベスト・プラクティスとしてクラスの現行インスタンスでプロパティーまたはメソッドを呼び出すときはいつでもthisキーワードを使用することです。

私は、ベースと子の方法でこのあいまいさがコンパイラの警告(エラーではないにしても)を上げていない場合でも心配しますが、それが見えない場合は私が推測します。

========================================== ================

編集:これらのリンクからの抜粋サンプルの例を以下に考えてみましょう:

http://geekswithblogs.net/BlackRabbitCoder/archive/2011/07/28/c.net-little-pitfalls-default-parameters-are-compile-time-substitutions.aspx

http://geekswithblogs.net/BlackRabbitCoder/archive/2010/06/17/c-optional-parameters---pros-and-pitfalls.aspx

Pitfall:オプションのパラメータ値はコンパイル時オプションのパラメータを使用するときに留意すべきことが1つだけあります。 この1つのことを念頭に置いておけば、潜在的な潜在的な落とし穴を理解し、回避する可能性があります。その1つは、オプションパラメータはコンパイル時の構文砂糖です!

落とし穴:継承とインターフェイス実装におけるデフォルトパラメータに注意

今、第2の潜在的な落とし穴は、継承とインターフェースの実装と関係しています。 私はパズルで説明します:

   1: public interface ITag 
   2: {
   3:     void WriteTag(string tagName = "ITag");
   4: } 
   5:  
   6: public class BaseTag : ITag 
   7: {
   8:     public virtual void WriteTag(string tagName = "BaseTag") { Console.WriteLine(tagName); }
   9: } 
  10:  
  11: public class SubTag : BaseTag 
  12: {
  13:     public override void WriteTag(string tagName = "SubTag") { Console.WriteLine(tagName); }
  14: } 
  15:  
  16: public static class Program 
  17: {
  18:     public static void Main() 
  19:     {
  20:         SubTag subTag = new SubTag();
  21:         BaseTag subByBaseTag = subTag;
  22:         ITag subByInterfaceTag = subTag; 
  23:  
  24:         // what happens here?
  25:         subTag.WriteTag();       
  26:         subByBaseTag.WriteTag(); 
  27:         subByInterfaceTag.WriteTag(); 
  28:     }
  29: } 

何が起こるのですか? タグが "SubTag"のSubTagであるにもかかわらず、オブジェクトは次のようになります。

1:SubTag 2:BaseTag 3:ITag

しかし、あなたに確かめることを忘れないでください:

既存のデフォルトパラメータのセットの真ん中に新しいデフォルトパラメータを挿入しないでください。これにより、リストの末尾に追加したり、新しいメソッドを作成したりする構文エラーが発生するとは限りません。 継承階層およびインターフェイスでデフォルトパラメータをどのように使用するかは非常に注意してください。予想される使用量に基づいてデフォルトを追加する最も適切なレベルを選択してください。

========================================== ============




@Marc Gravellと一般に同意する。

しかし、この問題はC ++の世界で十分に古く( http://www.devx.com/tips/Tip/12737 )、その答えは「実行時に解決される仮想関数とは違いますデフォルトの引数は静的に、つまりコンパイル時に解決されます。 だから、このC#コンパイラの動作は、予期せぬことにもかかわらず、一貫性のため意図的に受け入れられていたようです。




やってみました:

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

したがって、実際にはオーバーライドされたメソッドを使用するようにプログラムに指示します。