もくじ
https://tera1707.com/entry/2022/02/06/144447
やりたいこと
仕事で読むコードに「interfaceの明示的実装」が出てきたが、 以前理解したはずなのに、またアレ?となった。
もう忘れないようにまとめておきたい。
前提
- VisualStudio2022
- .NET6
コードと資料
コード
https://github.com/tera1707/InterfaceJikken
資料
実験①
using System.Diagnostics; namespace InterfaceJikken { internal class Program { static void Main(string[] args) { { MyClass1 mc1 = new MyClass1(); MyInterface1 mi1 = new MyClass1(); //mc1.Method1_1(); //mc1.Method1_2(); //((MyInterface1)mc1).Method1_1(); //((MyInterface1)mc1).Method1_2(); } { MyClass2 mc2 = new MyClass2(); MyInterface2 mi2 = new MyClass2(); Debug.WriteLine("mc2"); mc2.Method1_1(); mc2.Method1_2(); Debug.WriteLine("mc2 class"); ((MyClass1)mc2).Method1_1(); ((MyClass1)mc2).Method1_2(); ((MyClass2)mc2).Method1_1();//不要なキャスト ((MyClass2)mc2).Method1_2();//不要なキャスト Debug.WriteLine("mc2 if"); ((MyInterface1)mc2).Method1_1(); ((MyInterface1)mc2).Method1_2(); ((MyInterface2)mc2).Method1_1(); ((MyInterface2)mc2).Method1_2(); Debug.WriteLine("mi2"); mi2.Method1_1(); mi2.Method1_2(); Debug.WriteLine("mi2 class"); ((MyClass1)mi2).Method1_1(); ((MyClass1)mi2).Method1_2(); ((MyClass2)mi2).Method1_1(); ((MyClass2)mi2).Method1_2(); Debug.WriteLine("mi2 if"); ((MyInterface1)mi2).Method1_1();//不要なキャスト ((MyInterface1)mi2).Method1_2();//不要なキャスト ((MyInterface2)mi2).Method1_1();//不要なキャスト ((MyInterface2)mi2).Method1_2();//不要なキャスト } } } internal interface MyInterface1 { void Method1_1(); void Method1_2(); } internal interface MyInterface2 : MyInterface1 { void Method2_1(); } internal class MyClass1 : MyInterface1 { public void Method1_1() => Debug.WriteLine(" Method1_1 of MyClass1"); public void Method1_2()=> Debug.WriteLine(" Method1_2 of MyClass1"); } internal class MyClass2 : MyClass1, MyInterface2 { public new void Method1_1() => Debug.WriteLine(" Method1_1 of MyClass2"); public void Method1_2() => Debug.WriteLine(" Method1_2 of MyClass2"); public void Method2_1() => Debug.WriteLine(" Method2_1 of MyClass2"); } }
結果
mc2 Method1_1 of MyClass2 Method1_2 of MyClass2 mc2 class Method1_1 of MyClass1 Method1_2 of MyClass1 Method1_1 of MyClass2 Method1_2 of MyClass2 mc2 if Method1_1 of MyClass2 Method1_2 of MyClass2 Method1_1 of MyClass2 Method1_2 of MyClass2 mi2 Method1_1 of MyClass2 Method1_2 of MyClass2 mi2 class Method1_1 of MyClass1 Method1_2 of MyClass1 Method1_1 of MyClass2 Method1_2 of MyClass2 mi2 if Method1_1 of MyClass2 Method1_2 of MyClass2 Method1_1 of MyClass2 Method1_2 of MyClass2
※newを付けてくださいという警告(CS0108)が出ている状態と、newをつけて警告が出なくなった状態は、実行結果は同じ。(当たり前か)
実験② MyClass2の中のMethod1_1の実装を、MyInterface1の明示的な実装にしてみる
コード
using System.Diagnostics; namespace InterfaceJikken { internal class Program { static void Main(string[] args) { { MyClass1 mc1 = new MyClass1(); MyInterface1 mi1 = new MyClass1(); //mc1.Method1_1(); //mc1.Method1_2(); //((MyInterface1)mc1).Method1_1(); //((MyInterface1)mc1).Method1_2(); } { MyClass2 mc2 = new MyClass2(); MyInterface2 mi2 = new MyClass2(); Debug.WriteLine("mc2"); mc2.Method1_1(); mc2.Method1_2(); Debug.WriteLine("mc2 class"); ((MyClass1)mc2).Method1_1(); ((MyClass1)mc2).Method1_2(); ((MyClass2)mc2).Method1_1();//不要なキャスト ((MyClass2)mc2).Method1_2();//不要なキャスト Debug.WriteLine("mc2 if"); ((MyInterface1)mc2).Method1_1(); ((MyInterface1)mc2).Method1_2(); ((MyInterface2)mc2).Method1_1(); ((MyInterface2)mc2).Method1_2(); Debug.WriteLine("mi2"); mi2.Method1_1(); mi2.Method1_2(); Debug.WriteLine("mi2 class"); ((MyClass1)mi2).Method1_1(); ((MyClass1)mi2).Method1_2(); ((MyClass2)mi2).Method1_1(); ((MyClass2)mi2).Method1_2(); Debug.WriteLine("mi2 if"); ((MyInterface1)mi2).Method1_1();//不要なキャスト ((MyInterface1)mi2).Method1_2();//不要なキャスト ((MyInterface2)mi2).Method1_1();//不要なキャスト ((MyInterface2)mi2).Method1_2();//不要なキャスト } } } internal interface MyInterface1 { void Method1_1(); void Method1_2(); } internal interface MyInterface2 : MyInterface1 { void Method2_1(); } internal class MyClass1 : MyInterface1 { public void Method1_1() => Debug.WriteLine(" Method1_1 of MyClass1"); public void Method1_2()=> Debug.WriteLine(" Method1_2 of MyClass1"); } internal class MyClass2 : MyClass1, MyInterface2 { void MyInterface1.Method1_1() => Debug.WriteLine(" Method1_1 of MyClass2"); // ★ココが①と違う public void Method1_2() => Debug.WriteLine(" Method1_2 of MyClass2"); public void Method2_1() => Debug.WriteLine(" Method2_1 of MyClass2"); } }
結果
mc2 Method1_1 of MyClass1 //★ココが①と違う → MyClass2の中で、MyInterface1のMethod1_1の実装をもう一回やってる感じということか Method1_2 of MyClass2 mc2 class Method1_1 of MyClass1 Method1_2 of MyClass1 Method1_1 of MyClass1 //★ココが①と違う Method1_2 of MyClass2 mc2 if Method1_1 of MyClass2 Method1_2 of MyClass2 Method1_1 of MyClass2 Method1_2 of MyClass2 mi2 Method1_1 of MyClass2 Method1_2 of MyClass2 mi2 class Method1_1 of MyClass1 Method1_2 of MyClass1 Method1_1 of MyClass1 //★ココが①と違う Method1_2 of MyClass2 mi2 if Method1_1 of MyClass2 Method1_2 of MyClass2 Method1_1 of MyClass2 Method1_2 of MyClass2
実験②では、mc2をMyClass1にキャストしてから、MyClass2の中で明示的にMyInterface1のメソッドとして実装したMyMethod1_1を呼ぼうとすると、不要なキャストとしてMsgがでる。
↓薄い色になって、不要なキャストと言われる
キャストはいらないということは、つまり、
「明示的にMyInterface1のメソッドとしてMyMethod1_1を実装することは、MyClass1に書いたMyMethod1_1の実装を、MyClass2でやり直しているだけ。」
だから、実質、
「MyClass2には、MyMethod1_1の実装はない!」
ということか。
実験③ Method1_1を、MyClass1ではvirtualにし、MyClass2でoverrideする
コード
using System.Diagnostics; namespace InterfaceJikken { internal class Program { static void Main(string[] args) { { MyClass1 mc1 = new MyClass1(); MyInterface1 mi1 = new MyClass1(); //mc1.Method1_1(); //mc1.Method1_2(); //((MyInterface1)mc1).Method1_1(); //((MyInterface1)mc1).Method1_2(); } { MyClass2 mc2 = new MyClass2(); MyInterface2 mi2 = new MyClass2(); Debug.WriteLine("mc2"); mc2.Method1_1(); mc2.Method1_2(); Debug.WriteLine("mc2 class"); ((MyClass1)mc2).Method1_1(); ((MyClass1)mc2).Method1_2(); ((MyClass2)mc2).Method1_1();//不要なキャスト ((MyClass2)mc2).Method1_2();//不要なキャスト Debug.WriteLine("mc2 if"); ((MyInterface1)mc2).Method1_1(); ((MyInterface1)mc2).Method1_2(); ((MyInterface2)mc2).Method1_1(); ((MyInterface2)mc2).Method1_2(); Debug.WriteLine("mi2"); mi2.Method1_1(); mi2.Method1_2(); Debug.WriteLine("mi2 class"); ((MyClass1)mi2).Method1_1(); ((MyClass1)mi2).Method1_2(); ((MyClass2)mi2).Method1_1(); ((MyClass2)mi2).Method1_2(); Debug.WriteLine("mi2 if"); ((MyInterface1)mi2).Method1_1();//不要なキャスト ((MyInterface1)mi2).Method1_2();//不要なキャスト ((MyInterface2)mi2).Method1_1();//不要なキャスト ((MyInterface2)mi2).Method1_2();//不要なキャスト } } } internal interface MyInterface1 { void Method1_1(); void Method1_2(); } internal interface MyInterface2 : MyInterface1 { void Method2_1(); } internal class MyClass1 : MyInterface1 { public virtual void Method1_1() => Debug.WriteLine(" Method1_1 of MyClass1"); // ★ココが②と違う public void Method1_2()=> Debug.WriteLine(" Method1_2 of MyClass1"); } internal class MyClass2 : MyClass1, MyInterface2 { public override void Method1_1() => Debug.WriteLine(" Method1_1 of MyClass2"); // ★ココが②と違う public void Method1_2() => Debug.WriteLine(" Method1_2 of MyClass2"); public void Method2_1() => Debug.WriteLine(" Method2_1 of MyClass2"); } }
結果
mc2 Method1_1 of MyClass2 // ★ココが②と違う ①とは同じ Method1_2 of MyClass2 mc2 class Method1_1 of MyClass2 // ★ココが②と違う ①とも違う! Method1_2 of MyClass1 Method1_1 of MyClass2 // ★ココが②と違う ①とは同じ Method1_2 of MyClass2 mc2 if Method1_1 of MyClass2 Method1_2 of MyClass2 Method1_1 of MyClass2 Method1_2 of MyClass2 mi2 Method1_1 of MyClass2 Method1_2 of MyClass2 mi2 class Method1_1 of MyClass2 // ★ココが②と違う ①とは同じ Method1_2 of MyClass1 Method1_1 of MyClass2 // ★ココが②と違う ①とは同じ Method1_2 of MyClass2 mi2 if Method1_1 of MyClass2 Method1_2 of MyClass2 Method1_1 of MyClass2 Method1_2 of MyClass2
親のクラスでvirtualとして実装されたメソッドをoverrideすると、
呼ぶときに親(MyClass1)として呼ばれようが子(MyClass2)として呼ばれようが、overrideしたほうの子のメソッドが呼ばれる様子。
まとめ
そして、一番気になる、newとvirtual/overrideのやり方の違い(なんで、似たような機能がC#にあるのか?)が、下記ページに書かれていた。
https://atmarkit.itmedia.co.jp/fdotnet/csharp_abc/csharp_abc_004/csharp_abc02.html
こまかいこと
子クラスに親クラスと同じメソッドを定義したときに出てくる下記コメントの「非表示」は、英語だと「Hide」。
非表示というとよくわからないが、Hideだと「親のメソッドを隠す」ということで分かりやすい気がした。
参考
第4回 継承とインターフェイス
わかりやすい!
https://atmarkit.itmedia.co.jp/fdotnet/csharp_abc/csharp_abc_004/csharp_abc02.html
第5回に、インターフェースの話が書かれいてる。
これも、ずっと前からのギモン(抽象クラスとインターフェースがどう違うのか?似てるものが2個あるのはなぜ?)に触れてるようなので、見てみる。
https://atmarkit.itmedia.co.jp/fdotnet/csharp_abc/csharp_abc_004/csharp_abc03.html