私はこのブログ投稿を見ていて、次の質問がありました。
new
キーワードが必要なのは、基本クラスのメソッドが非表示になっていることを指定するためだけです。つまり、なぜそれが必要なのですか?override
キーワードを使わないのなら、基本クラスのメソッドを隠していませんか?- C#のデフォルトが非表示でオーバーライドされないのはなぜですか?なぜ設計者はそれをこのように実装したのですか?
私はこのブログ投稿を見ていて、次の質問がありました。
new
キーワードが必要なのは、基本クラスのメソッドが非表示になっていることを指定するためだけです。つまり、なぜそれが必要なのですか?override
キーワードを使わないのなら、基本クラスのメソッドを隠していませんか?回答:
良い質問です。それらを言い換えさせてください。
メソッドを別のメソッドで非表示にすることが合法なのはなぜですか?
その質問に例を挙げて答えさせてください。CLR v1のインターフェイスがあります:
interface IEnumerable
{
IEnumerator GetEnumerator();
}
素晴らしい。今、CLRにのみ、我々は、私は、この汎用インタフェースただろうV1にジェネリックを持っていたのだ場合。しかし、私はしませんでした。私は今、それに対応して何かを作る必要があり、あなたはジェネリックを持っていて、」男だと思いv2のある一般的なようにIEnumerableを期待するコードとの下位互換性を失うことなく、ジェネリックスのメリットを享受できます。」
interface IEnumerable<T> : IEnumerable
{
IEnumerator<T> .... uh oh
のGetEnumeratorメソッドを何と呼びIEnumerable<T>
ますか?非ジェネリックベースインターフェイスでGetEnumeratorを非表示にする必要があることを忘れないでください。明示的に後方互換の状況にない限り、そのようなものが呼び出されることは決してありません。
それだけでメソッドの非表示が正当化されます。メソッド非表示の正当化に関するその他の考えについては、このテーマに関する私の記事を参照してください。
「新しい」なしで非表示にすると警告が発生するのはなぜですか?
あなたが何かを隠していて、それを誤って行っている可能性があることをあなたに知らせたいからです。派生クラスを編集するのではなく、他の誰かが基本クラスを編集したために、誤って何かを隠している可能性があることを忘れないでください。
エラーではなく「新しい」警告なしで非表示になっているのはなぜですか?
同じ理由。基本クラスの新しいバージョンを取得したばかりであるため、誤って何かを隠している可能性があります。これは常に起こります。FooCorpは基本クラスBを作成します。BarCorpはメソッドBarを使用して派生クラスDを作成します。これは、顧客がそのメソッドを気に入っているためです。FooCorpはそれを見て、ちょっといい考えだと言います。その機能を基本クラスに置くことができます。彼らはそうし、Foo.DLLの新しいバージョンを出荷します。そして、BarCorpが新しいバージョンを取得するときに、メソッドが基本クラスのメソッドを非表示にするように言われたらいいのですが。
エラーにすることは、これが脆弱な基本クラスの問題の別の形式であることを意味するため、この状況をエラーではなく警告にする必要があります。C#は、誰かが基本クラスに変更を加えたときに、派生クラスを使用するコードへの影響が最小限に抑えられるように慎重に設計されています。
デフォルトを非表示にしてオーバーライドしないのはなぜですか?
仮想オーバーライドは危険だからです。仮想オーバーライドにより、派生クラスは、基本クラスを使用するようにコンパイルされたコードの動作を変更できます。オーバーライドを行うなどの危険なことを行うことは、偶然ではなく、意識的かつ意図的に行うことです。
このコンテキストでの唯一の効果はnew
、警告を抑制することであることに注意してください。セマンティクスに変更はありません。
したがって、1つの答えは次のとおりです。非表示new
が意図的なものであることをコンパイラに通知し、警告を取り除く必要があります。
フォローアップの質問は次のとおりです。メソッドをオーバーライドしない/オーバーライドできない場合、なぜ同じ名前の別のメソッドを導入するのでしょうか。隠蔽は本質的に名前の衝突だからです。そしてもちろん、ほとんどの場合それを避けます。
私が意図的に隠すために考えることができる唯一の正当な理由は、名前がインターフェースによってあなたに強制されるときです。
C#では、メンバーはデフォルトで封印されています。つまり、パフォーマンス上の理由から、メンバーをオーバーライドすることはできません(virtual
またはabstract
キーワードでマークされていない限り)。新しい修飾子は、明示的に継承されたメンバーを隠すために使用されます。
override
キーワードを指定せずにオーバーライドがデフォルトであった場合、名前が等しいという理由だけで、誤ってベースのメソッドをオーバーライドする可能性があります。
.Netコンパイラの戦略は、安全のために、問題が発生した場合に警告を発することです。したがって、この場合、オーバーライドがデフォルトの場合、オーバーライドされたメソッドごとに警告が必要になります。「警告:本当に必要かどうかを確認してください。 'をオーバーライドします。
私の推測では、主に複数のインターフェイスの継承が原因です。目立たないインターフェイスを使用すると、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
}
}
new
キーワードを使用すると、その時点からすべての子の継承ベースを変更することもできます。
前述のように、メソッド/プロパティの非表示により、他の方法では簡単に変更できなかったメソッドまたはプロパティに関する変更が可能になります。これが役立つ状況の1つは、継承されたクラスが基本クラスで読み取り専用の読み取り/書き込みプロパティを持つことを許可することです。たとえば、基本クラスにValue1-Value40という読み取り専用プロパティが多数あるとします(もちろん、実際のクラスはより適切な名前を使用します)。このクラスの封印された子孫には、基本クラスのオブジェクトを受け取り、そこから値をコピーするコンストラクターがあります。クラスでは、その後の変更は許可されていません。別の継承可能な子孫は、Value1-Value40と呼ばれる読み取り/書き込みプロパティを宣言します。これは、読み取られると基本クラスのバージョンと同じように動作しますが、書き込まれると値を書き込むことができます。
このアプローチの1つの厄介な点は、おそらく誰かが私を助けてくれるかもしれませんが、同じクラス内の特定のプロパティをシャドウイングおよびオーバーライドする方法がわからないことです。CLR言語のいずれかでそれが許可されていますか(私はvb 2005を使用しています)?基本クラスオブジェクトとそのプロパティが抽象であると便利ですが、子孫クラスがそれらをシャドウする前に、中間クラスがValue1からValue40プロパティをオーバーライドする必要があります。