どのようにメソッドの隠蔽は、C#で動作しますか? (パート2)
-
22-08-2019 - |
質問
次のプログラムを印刷
A:C(A,B)
B:C(A,B)
(それが必要として)
public interface I
{
string A();
}
public class C : I
{
public string A()
{
return "A";
}
public string B()
{
return "B";
}
}
public class A
{
public virtual void Print(C c)
{
Console.WriteLine("A:C(" + c.A() + "," + c.B() + ")");
}
}
public class B : A
{
public new void Print(C c)
{
Console.WriteLine("B:C(" + c.A() + "," + c.B() + ")");
}
public void Print(I i)
{
Console.WriteLine("B:I(" + i.A() + ")");
}
}
class Program
{
public static void Main(string[] args)
{
A a = new A();
B b = new B();
C c = new C();
a.Print(c);
b.Print(c);
}
}
しかし、私はそうのようなクラスBに「オーバーライド」にキーワード「新しい」を変更する場合:
public override void Print(C c)
突然プログラムのすべては、印刷に始まります:
A:C(A,B)
B:I(A)
なぜ?
解決
この問題は解決されている方法をオーバーロードする方法で行うことです。
効果的に(多少簡略化)、コンパイラは、まず、この場合、式(B)の宣言された型を見て、候補となる方法にを探し最初ののその型内で宣言されています。適切な任意の方法は、(すなわち、すべての引数はメソッドのパラメータの型に変換することができる場所)がある場合、それは、のは任意の親の種類を見ていません。これは、派生型のいずれかの「たて宣言」適切な方法が存在する場合、最初の宣言は、親の型であるオーバーライドされたメソッドは、ルックインを取得しないことを意味します。
ここで少しシンプルな例です。
using System;
class Base
{
public virtual void Foo(int x)
{
Console.WriteLine("Base.Foo(int)");
}
}
class Derived : Base
{
public override void Foo(int x)
{
Console.WriteLine("Derived.Foo(int)");
}
public void Foo(double d)
{
Console.WriteLine("Derived.Foo(double)");
}
}
class Test
{
static void Main()
{
Derived d = new Derived();
d.Foo(10);
}
}
このはDerived.Foo(double)
を印刷します - コンパイラが型int
のパラメータと一致するメソッドがあることを知っている、と引数はint
を入力し、int
からint
への変換はint
するdouble
からの変換よりも「より良い」であっても、唯一Foo(double)
方法がもともとあるという事実は、は<は/ em>のDerived
では、コンパイラはFoo(int)
を無視する意味宣言します。
これは、IMO非常に驚くべきことです。それ以外の場合は予期せぬ動作を変更することができ、基本クラスの新しい、より具体的な、方法を導入 - - 私はDerived
がFoo
をオーバーライドしなかった場合、それはケースのようになり、なぜ見ることができますが、明らかに、ここでDerived
はBase.Foo(int)
についての知っていますそれをオーバーライドます。これは私がC#の設計者が間違った決定をしたと信じて(比較的少ない)ポイントの一つです。
他のヒント
オクラホマので、
public new void Print(C c)
{
Console.WriteLine("B:C(" + c.A() + "," + c.B() + ")");
}
public void Print(I i)
{
Console.WriteLine("B:I(" + i.A() + ")");
}
これは、印刷のための新しい方法を宣言します。 BはAから継承するため今、あなたはsimly二回新しいメソッドを呼び出しています。あなたがメソッドをオーバライドするとき、あなたがAのために呼び出したとき、これは、メソッドのシグネチャを変更しますが、あなたはBの署名を呼び出すとき、それは、独自のメソッドシグネチャを持っています。
私は明確に良い質問を説明していますかどうかわからないです。
新しい使用します:
AとBは、印刷方法の同じ実装を取得します。
オーバーライドを使用して だけAにBのメソッドシグネチャを変更していない、のように、
Aは、Bとは異なるメソッドシグネチャを有している。
それは基本的にはこれを無視し、新しいを使用します:
public void Print(I i)
{
Console.WriteLine("B:I(" + i.A() + ")");
}
これは素晴らしい質問だった。
すべての答えは、ここで見つけることができます:
http://msdn.microsoft.com/en-us /library/6fawty39(VS.80).aspxする
それの要旨はこれです:
... C#コンパイラは、最初に作成しようとします バージョンとの互換性コール [functionNameを]元々の宣言 [派生クラス]。オーバーライドメソッドではありません クラスで宣言として考え、 彼らは、新しい実装であります この方法は、基本クラスに宣言しました。のみ C#コンパイラは一致しないことができる場合 上の元のメソッドへのメソッド呼び出し [派生クラス]それは、コールに一致するようにしようとします 同じでオーバーライドされたメソッドへ 名前と互換性のあるパラメータます。
(cはIを実装しているため)あなたは、引数「C」にマッチ派生クラスに新しいメソッドを印刷(I i)を持っているので、その方法は、「オーバーライド」の方法よりも優先されますので。
は、あなたが「新しい」としてメソッドをマークすると、彼らは両方の派生クラスに実装されると考えられ、それが優先されますので、印刷(C c)は方法がより密接に、パラメータ「c」をマッチします。
これは、少なくとも同じくらいの方法は、のオーバーロードのC#でどのように機能するかについての質問です。私はあなたがここに興味深い状況を強調したと思う...
最初のケースでは(メソッドにnew
キーワードを使用して)、コンパイラは、それの型が渡されたパラメータと正確に同等であるので、タイプCのパラメータでPrint
メソッドオーバーロードを使用することを決定する(すなわち、暗黙的な変換ではありません。言い換えれば、それはより多くの「明白な」メソッドオーバーロードを選択します。
Print
方法を選択した場合、私は必要とされるであろうインターフェースへの暗黙的な変換に対し)必要な
第二の場合(メソッドにoverride
キーワードを使用して)、コンパイラーは、クラスBにPrint
メソッドオーバーロードをオーバーライドしているが、それは効果的に定義されているので、型のパラメータIとPrint(C c)
のオーバーロードを使用することを決定しますコンパイラは最初に見つかっすなわち実際にPrint(I i)
メソッドオーバーロード、最高レベルの過負荷、したがって、最も直接的なものを、作る親クラスA、ます。
うまくいけば、これはあなたが理解するのに役立ちます。私はさらに、任意のポイントをclairfyする必要がある場合、私に教えてください...
注:私は、コンパイラはこれらの事をすると言っについて間違っている場合は、私を修正してください、それはそれは、コンパイラやCLR / JITのかどうか議論のために少し違いますが、それは思わ。 P>