web-dev-qa-db-ja.com

なぜ新しいキーワードが必要なのか、なぜデフォルトの動作が非表示で上書きしないのか?

私はこれを見ていました ブログ投稿 と次の質問がありました:

  • newキーワードが必要な理由は、基本クラスのメソッドが非表示であることを指定するためだけです。つまり、なぜそれが必要なのでしょうか。 overrideキーワードを使用しない場合、基本クラスのメソッドを非表示にしませんか?
  • なぜC#のデフォルトが非表示でオーバーライドされないのですか?なぜデザイナーはそれをこのように実装したのですか?
80
Sandbox

良い質問です。もう一度言いましょう。

メソッドを別のメソッドで非表示にすることが合法であるのはなぜですか?

例を挙げてその質問に答えましょう。 CLR v1からのインターフェイスがあります。

interface IEnumerable
{
    IEnumerator GetEnumerator();
}

素晴らしい。今CLR v2にはジェネリックがあり、「v1にジェネリックがあったとしたら、これをジェネリックインターフェイスにしたでしょう。しかし、そうしませんでした。互換性を持たせる必要がありますisジェネリックなので、IEnumerableを期待するコードとの下位互換性を失うことなくジェネリックの利点を得ることができます。

interface IEnumerable<T> : IEnumerable
{
    IEnumerator<T> .... uh oh

IEnumerable<T>のGetEnumeratorメソッドを何と呼びますか?非ジェネリックの基本インターフェースでGetEnumeratorを非表示にしたいwantことを覚えておいてください。後方互換性のある状況に明示的にいない限り、あなたはneverと呼ばれることを望みません。

それだけでメソッドの非表示を正当化できます。メソッドの非表示の正当化に関するより多くの考えについては この件に関する私の記事 を参照してください。

「新規」なしで非表示にすると警告が表示されるのはなぜですか?

私たちはあなたが何かを隠していて、誤ってそれをしているかもしれないということをあなたの注意に伝えたいので。 派生クラスを編集するのではなく、他の誰かがベースクラスを編集したため、誤って何かを非表示にしている可能性があることを忘れないでください。

エラーではなく警告を「新しい」警告なしで非表示にするのはなぜですか?

同じ理由。基本クラスの新しいバージョンを取得したばかりなので、誤って何かを非表示にしている可能性があります。これは常に起こります。 FooCorpは基本クラスBを作成します。BarCorpは、顧客がメソッドを好むため、メソッドBarを使用して派生クラスDを作成します。 FooCorpはそれを見て、いいことだと言っています。これは、その機能を基本クラスに置くことができます。彼らはそうし、Foo.DLLの新しいバージョンを出荷します。BarCorpが新しいバージョンを入手したときに、メソッドが基本クラスのメソッドを非表示にするように言われたらいいでしょう。

その状況をwarningにして、errorにしたくないので、エラーは、これがもろい基本クラスの問題の別の形式であることを意味しますC#は、誰かが基本クラスに変更を加えたときに、派生クラスを使用するコードへの影響が最小限になるように注意深く設計されています。

デフォルトを非表示にしてオーバーライドしないのはなぜですか?

なぜなら、仮想オーバーライドはdangerousだからです。仮想オーバーライドにより、派生クラスは、基本クラスを使用するようにコンパイルされたコードの動作を変更できます。オーバーライドなどの危険なことは、意識的に意図的に )、偶然ではありません。

125
Eric Lippert

派生クラスのメソッドの前に新しいキーワードがある場合、そのメソッドは、基本クラスのメソッドから独立していると定義されます。

ただし、newまたはoverridesのいずれも指定しない場合、結果の出力はnewを指定した場合と同じですが、コンパイラの警告が表示されます(基本クラスのメソッドでメソッドを非表示にしていることに気付かない場合があるため、または、実際にそれを上書きしたかっただけで、キーワードを含めるのを忘れた可能性があります)。

そのため、間違いを避け、やりたいことを明確に示すのに役立ち、コードが読みやすくなるので、コードを簡単に理解できます。

14
Incognito

このコンテキストでのnewonly効果は警告を抑制することであることは注目に値します。セマンティクスに変更はありません。

したがって、1つの答えは、隠蔽がintentionalであることをコンパイラに通知するためにnewが必要であり、警告を取り除きます。

フォローアップの質問は次のとおりです。メソッドをオーバーライドできない、またはオーバーライドできない場合、なぜ同じ名前の別のメソッドを導入するのですか?隠すことは本質的に名前の衝突だからです。もちろん、ほとんどの場合はそれを避けます。

意図的に隠すために私が考えることができる唯一の正当な理由は、名前がインターフェースによってあなたに強制されるときです。

12
Henk Holterman

C#では、メンバーはデフォルトでシールされています。つまり、それらをオーバーライドすることはできません(virtualまたはabstractキーワードでマークされていない限り)および これはパフォーマンス上の理由から新しい修飾子 は、継承されたメンバーを明示的に非表示にするために使用されます。

5
Darin Dimitrov

オーバーライドがoverrideキーワードを指定せずにデフォルトであった場合、名前が等しいためにベースのメソッドを誤ってオーバーライドしてしまう可能性があります。

.Netコンパイラの戦略は、問題が発生した場合に警告を発して安全を確保することです。この場合、オーバーライドがデフォルトの場合、オーバーライドされたメソッドごとに警告が必要になります-「警告:本当に必要かどうかを確認オーバーライドする」。

2

私の推測は、主に複数のインターフェースの継承によるものでしょう。目立たないインターフェイスを使用すると、2つの異なるインターフェイスが同じメソッドシグネチャを使用する可能性が非常に高くなります。 newキーワードの使用を許可すると、2つの異なるクラスを作成する代わりに、1つのクラスでこれらの異なる実装を作成できます。

更新済み ...エリックは、この例を改善する方法についてアイデアをくれました。

public interface IAction1
{
    int DoWork();
}
public interface IAction2
{
    string DoWork();
}

public class MyBase : IAction1
{
    public int DoWork() { return 0; }
}
public class MyClass : MyBase, IAction2
{
    public new string DoWork() { return "Hi"; }
}

class Program
{
    static void Main(string[] args)
    {
        var myClass = new MyClass();
        var ret0 = myClass.DoWork(); //Hi
        var ret1 = ((IAction1)myClass).DoWork(); //0
        var ret2 = ((IAction2)myClass).DoWork(); //Hi
        var ret3 = ((MyBase)myClass).DoWork(); //0
        var ret4 = ((MyClass)myClass).DoWork(); //Hi
    }
}
0
Matthew Whited

前述のように、メソッド/プロパティを非表示にすると、メソッドまたはプロパティについて、他の方法では簡単に変更できないものを変更できます。これが役立つ状況の1つは、継承されたクラスが、基本クラスで読み取り専用の読み取り/書き込みプロパティを持つことを許可することです。たとえば、基本クラスにValue1-Value40と呼ばれる読み取り専用プロパティがたくさんあるとします(もちろん、実際のクラスはより適切な名前を使用します)。このクラスのシールされた子孫には、基本クラスのオブジェクトを取得し、そこから値をコピーするコンストラクターがあります。クラスは、それらがその後変更されることを許可しません。別の継承可能な子孫がValue1-Value40と呼ばれる読み取り/書き込みプロパティを宣言します。これは、読み取られると、基本クラスのバージョンと同じように動作しますが、書き込まれると、値を書き込むことができます。最終的な効果は、変更されないことがわかっている基本クラスのインスタンスを必要とするコードが読み取り専用クラスの新しいオブジェクトを作成できることです。これにより、渡されたオブジェクトからデータをコピーできます。読み取り専用または読み書き可能です。

このアプローチの1つの不快な点は、おそらく誰かが私を手助けしてくれることでしょう-同じクラス内の特定のプロパティをシャドウイングおよびオーバーライドする方法がわからないことです。 CLR言語のいずれかで許可されていますか(私はvb 2005を使用しています)?基本クラスオブジェクトとそのプロパティが抽象的である場合に役立ちますが、子孫クラスがそれらをシャドウする前に、中間クラスがValue1からValue40プロパティをオーバーライドする必要があります。

0
supercat