.Netのむンタヌフェむスに぀いお少し1぀のむンタビュヌに基づいお

先週の月曜日、私は幞運なこずに、ある囜際䌁業のシニア.Net開発者のむンタビュヌを受けたした。 むンタビュヌ䞭に、.Netに関連するいく぀かの質問があるテストを受けるように頌たれたした。 特に、質問の1぀では、いく぀かのステヌトメントの評䟡true / falseを行う必芁がありたした。

.Netでは、int []などの芁玠の配列はデフォルトでIListを実装したす。これにより、foreachステヌトメントでコレクションずしお䜿甚できたす。


この質問にすばやく吊定的に答え、マヌゞンを個別に远加したす。 foreachにはIListではなくIEnumerableの実装が必芁であるため、次の質問に進みたした。 しかし、垰り道で、私は質問に苊しめられたした配列はただこのむンタヌフェヌスを実装しおいたすか

IListに぀いお、このむンタヌフェむスがIEnumerable、むンデクサヌ、コレクション内の芁玠の数を含むCountプロパティ、およびIsFixedCollectionなどのめったに䜿甚されないプロパティを提䟛したこずを挠然ず思い出したした。 配列にはサむズのLengthプロパティがあり、IEnumerableのCountはLINQの拡匵メ゜ッドであり、このメ゜ッドがクラスに実装されおいる堎合は䞍可胜です。 したがっお、配列がIListむンタヌフェむスを実装できなかったこずが刀明したしたが、挠然ずした気持ちに悩たされたした。 したがっお、むンタビュヌ埌の倕方、私は少し調査するこずにしたした。


クラスSystem.Array


Reflector.Netはむンストヌルされおいないため、敎数配列によっお実装されるむンタヌフェむスを調べるための短いプログラムをCで䜜成したした。

var v = new int[] { 1, 2, 3 }; var t = v.GetType(); var i = t.GetInterfaces(); foreach(var tp in i) Console.WriteLine(tp.Name); 


コン゜ヌルりィンドりから受信したむンタヌフェむスの完党なリストは次のずおりです。

 ICloneable IList ICollection IEnumerable IStructuralComparable IStructuralEquatable IList`1 ICollection`1 IEnumerable`1 IReadOnlyList`1 IReadOnlyCollection`1 


したがっお、 .Netの配列は、IListむンタヌフェむスずその汎甚バヌゞョンのIList <>をただ実装しおいたす 。

より完党な情報を取埗するために、System.Arrayクラスの図を䜜成したした。



私のミスはすぐに私を襲いたしたCountはIListのプロパティではなく、継承チェヌンの以前のむンタヌフェむスであるICollectionです。 ただし、このむンタヌフェむスの他のプロパティIsFixedSizeおよびIsReadOnlyが実装されおいたにもかかわらず、配列自䜓にはIListむンタヌフェむスの他の倚くのプロパティほどのプロパティはただありたせんでした。 これはどのように可胜ですか

Cでむンタヌフェむスを実装できるだけでなく、
暗黙的ですが、明瀺的にも。 私は教科曞からこの可胜性に぀いお知っおいたした。それはそのような実装の䟋を瀺したした。 基本クラスに既にむンタヌフェむスメ゜ッドず同じ名前のメ゜ッドが含たれおいる堎合。 ReSharperでもこのような機䌚を芋たした。 ただし、これたでのずころ、自分のプロゞェクトに明瀺的にむンタヌフェむスを実装する必芁性に盎接察凊する必芁はありたせんでした。

むンタヌフェむスの明瀺的実装ず暗黙的実装の比范


これら2皮類のむンタヌフェむスの実装を比范しおみたしょう。

基準
暗黙的な実装
明瀺的な実装
基本的な構文
 interface ITest { void DoTest(); } public class ImplicitTest : ITest { public void DoTest() { } } 

 interface ITest { void DoTest(); } public class ExplicitTest : ITest { void ITest.DoTest() { } } 


可芖性
暗黙的な実装は垞にパブリックであるため、メ゜ッドずプロパティに盎接アクセスできたす。
 var imp = new ImplicitTest(); imp.DoTest(); 

明瀺的な実装は垞に閉じられおいたすプラむベヌト。
実装にアクセスするには、クラスのむンスタンスをむンタヌフェむスにキャストする必芁がありたすむンタヌフェむスにアップキャスト。
 var exp = new ExplicitTest(); ((ITest)exp).DoTest(); 

倚態性
暗黙的なむンタヌフェむスの実装は仮想にするこずができたす。これにより、子孫クラスでこの実装を曞き換えるこずができたす。
明瀺的な実装は垞に静的です。 新しい子孫クラスでオヌバヌラむドたたはオヌバヌラむドするこずはできたせん。 ご泚意 1
抜象クラスず実装
暗黙の実装は抜象的であり、子孫クラスでのみ実装できたす。
明瀺的な実装は抜象的ではないかもしれたせんが、クラス自䜓は他の抜象メ゜ッドを持ち、抜象的であるかもしれたせん。 ご泚意 2

泚
ご泚意 1 - mayorovpがコメントで正しく芳察しおいるように、子孫クラスのむンタヌフェむスの明瀺的な実装を繰り返しお実装をオヌバヌラむドできたす蚘事の最初のコメントを参照。

ご泚意 2- ブログの1぀は、クラス自䜓を抜象化できないこずを瀺したした。 おそらくこれは、以前のバヌゞョンのコンパむラのいく぀かに圓おはたりたした。私の実隓では、抜象クラスで明瀺的にむンタヌフェむスを簡単に実装できたした。

なぜむンタヌフェむスの明瀺的な実装が必芁なのですか


MSDNによるず、クラスによっお実装された耇数のむンタヌフェむスに同じシグネチャを持぀メ゜ッドがある堎合、明瀺的なむンタヌフェむス実装が必芁です。 この問題は、䞀般的に英語圏では「死の臎呜的なダむダモンド」ずいう恐ろしい名前で知られおおり、ロシア語では「ダむダモンドの問題」ず蚳されおいたす。 そのような状況の䟋を次に瀺したす。

 /* Listing 1 */ interface IJogger { void Run(); } interface ISkier { void Run(); } public class Athlete: ISkier, IJogger { public void Run() { Console.WriteLine("Am I an Athlete, Skier or Jogger?"); } } 


ちなみに、この䟋はCの正しいコヌドです。぀たり、正しくコンパむルされお起動されたすが、Runメ゜ッドはクラス自䜓のメ゜ッドであり、すでに2぀のむンタヌフェむスの実装です。 したがっお、異なるむンタヌフェむスずクラス自䜓に察しお1぀の実装を䜿甚できたす。 次のコヌドでこれを確認できたす。

 /* Listing 2 */ var sp = new Athlete(); sp.Run(); (sp as ISkier).Run(); (sp as IJogger).Run(); 


このコヌドの結果は「アスリヌト、スキヌダヌ、ゞョガヌですか」ずなり、コン゜ヌルに3回衚瀺されたす。

ここで、3぀のケヌスすべおを分離するために、むンタヌフェむスの明瀺的な実装を䜿甚できたす。

 /* Listing 3 */ public class Sportsman { public virtual void Run() { Console.WriteLine("I am a Sportsman"); } } public class Athlete: Sportsman, ISkier, IJogger { public override void Run() { Console.WriteLine("I am an Athlete"); } void ISkier.Run() { Console.WriteLine("I am a Skier"); } void IJogger.Run() { Console.WriteLine("I am a Jogger"); } } 


この堎合、リスト2のコヌドを実行するず、コン゜ヌルに「私はアスリヌト」 、 「私はスキヌダヌ」 、 「私はゞョガヌ」ずいう 3行が衚瀺されたす。

さたざたなむンタヌフェむス実装の長所ず短所


実装の可芖性ずカスタム実装

䞊蚘のように、暗黙の実装は通垞のクラスメ゜ッドず構文的に違いはありたせんこのメ゜ッドが祖先クラスで既に定矩されおいる堎合、この構文ではメ゜ッドは子孫で非衚瀺になり、コヌドは問題なくコンパむルされたすcメ゜ッドの非衚瀺に関するコンパむラ譊告。。 さらに、1぀のむンタヌフェむスの個々のメ゜ッドの遞択的な実装は、明瀺的および暗黙的に可胜です。

 /* Listing 4 */ public class Code { public void Run() { Console.WriteLine("I am a class method"); } } interface ICommand { void Run(); void Execute(); } public class CodeCommand : Code, ICommand { // implicit interface method implementation // => public implementation // implicit base class method hiding (warning here) public void Run() { base.Run(); } // explicit interface method implementation // => private implementation void ICommand.Execute() {} } 


これにより、個別のむンタヌフェむスメ゜ッドの実装をネむティブクラスメ゜ッドずしお䜿甚でき、察応するむンタヌフェむスにキャストした埌にのみプラむベヌトで衚瀺されるメ゜ッドの明瀺的な実装ずは察照的に、たずえばIntelliSenseを介しお䜿甚できたす。

䞀方、メ゜ッドのプラむベヌト実装の可胜性により、完党に実装しながら倚くのむンタヌフェむスメ゜ッドを隠すこずができたす。 .Netの配列を䜿甚した最初の䟋に戻るず、配列は、たずえばICollectionむンタヌフェむスのCountプロパティの実装を隠し、このプロパティをLengthずいう名前で公開しおいるこずがわかりたすこれはおそらくC ++ STLおよびJavaずの互換性を維持するための詊みです。 したがっお、実装されたむンタヌフェむスの特定のメ゜ッドを非衚瀺にし、他のメ゜ッドを非衚瀺=公開にするこずはできたせん。

ただし、これらのむンタヌフェむスのメ゜ッドもプロパティもIntelliSenseで衚瀺されないため、倚くの堎合、クラスによっお実装されるむンタヌフェむスを「暗黙的に」掚枬するこずは完党に䞍可胜であるずいう問題が発生したすSystem.Arrayの䟋もここに瀺したす。 このような実装を怜出する唯䞀の方法は、たずえばVisual Studioのオブゞェクトブラりザヌを䜿甚しお、リフレクションを䜿甚するこずです。

むンタヌフェむスリファクタリング

むンタヌフェむスの暗黙的なパブリック実装は、パブリッククラスメ゜ッドの実装ず倉わらないため、むンタヌフェむスをリファクタリングし、パブリックメ゜ッドを削陀する堎合たずえば、䞊蚘のICommandむンタヌフェむスからRunおよびExecuteメ゜ッドを1぀のRun すべおの暗黙的な実装では、オヌプンアクセスメ゜ッドが残りたす。このパブリックメ゜ッドは、システムの他のコンポヌネントですでに異なる䟝存関係を持っおいる可胜性があるため、リファクタリング埌でもサポヌトする必芁がありそうです。 この結果、以前のむンタヌフェむスメ゜ッドの特定のおよび異なるクラス、おそらく異なる実装間で䟝存関係が既に存圚するため、「実装ではなくむンタヌフェむスに察しお」ずいうプログラミング原則に違反したす。

 /* Listing 5 */ interface IFingers { void Thumb(); void IndexFinger(); // an obsolete interface method // void MiddleFinger(); } public class HumanPalm : IFingers { public void Thumb() {} public void IndexFinger() {} // here is a "dangling" public method public void MiddleFinger() {} } public class AntropoidHand : IFingers { void IFingers.Thumb() {} void IFingers.IndexFinger() {} // here the compiler error void IFingers.MiddleFinger() {} } 


むンタヌフェむスのプラむベヌト実装の堎合、より存圚しないメ゜ッドの明瀺的な実装を持぀すべおのクラスは単にコンパむルを停止したすが、䞍芁になった実装を削陀した埌たたは新しいメ゜ッドにリファクタリングした埌、むンタヌフェむスにバむンドされおいない「䜙分な」パブリックメ゜ッドはありたせん。 もちろん、むンタヌフェむス自䜓ぞの䟝存関係をリファクタリングする必芁があるかもしれたせんが、少なくずも「実装ではなくむンタヌフェむスぞのプログラム」ずいう原則に違反するこずはありたせん。

プロパティに関しおは、暗黙的に実装されたむンタヌフェむスプロパティプロパティを䜿甚するず、倖郚およびクラス自䜓から盎接アクセサヌメ゜ッドゲッタヌずセッタヌを介しおアクセスできたす。これにより、䞍芁な効果たずえば、初期化䞭の䞍芁なデヌタ怜蚌プロパティ。

 /* Listing 6 */ interface IProperty { int Amount { get; set; } } public class ClassWithProperty : IProperty { // implicit implementation, public public int Amount { get; set; } public ClassWithProperty() { // internal invocation of the public setter Amount = 1000; } } public class ClassWithExplicitProperty : IProperty { // explicit implementation, private int IProperty.Amount { get; set; } public ClassWithExplicitProperty() { // internal invocation isn't possible // compiler error here Amount = 1000; } } 


むンタヌフェむスプロパティの明瀺的な実装では、これらのプロパティはプラむベヌトのたたであり、アクセスのために「長い」道を進み、初期化が行われる远加のプラむベヌトフィヌルドを宣蚀する必芁がありたす。 その結果、プロパティアクセスメ゜ッドが倖郚アクセスにのみ䜿甚される堎合、これによりコヌドが簡朔になりたす。

ロヌカル倉数ずクラスフィヌルドの明瀺的な型指定の䜿甚

むンタヌフェむスの明瀺的な実装の堎合、クラスのむンスタンスではなく、むンタヌフェむスのむンスタンスで䜜業しおいるこずを明瀺的に瀺す必芁がありたす。 したがっお、たずえば、型掚論を䜿甚しお、サヌビスワヌドvarを䜿甚しおCでロヌカル倉数を宣蚀するこずはできなくなりたす。 代わりに、ロヌカル倉数を宣蚀するずき、およびメ゜ッドのシグネチャずクラスのフィヌルドで、むンタヌフェむスのタむプを瀺す明瀺的な宣蚀を䜿甚する必芁がありたす。

したがっお、䞀方では、コヌドの柔軟性をいくらか䜎くしたすたずえば、ReSharperは、可胜であれば垞にvarを䜿甚した宣蚀の䜿甚を掚奚したすが、システムが倧きくなりサむズが倧きくなるに぀れお、特定の実装ぞのバむンドに関連する朜圚的な問題を回避したす。コヌド。 この点は倚くの物議をかもしおいるように思えるかもしれたせんが、プロゞェクトに取り組んでいる人や䞖界のさたざたな地域でさえ、明瀺的なタむピングを䜿甚するずコヌドの可読性が向䞊し、メンテナンスのコストが削枛されるため、非垞に䟿利です。

関連゜ヌス
蚘事の準備では、倚くのネットワヌク゜ヌス、特にブログ [1] 、 [2] 、 [3]および[4] からの情報、およびStackOverflowからの[5]および[6]の質問からの情報が䜿甚されたした。 CodeProjectおよびJeffrey Richterの「 C経由のCLR 」の13.5章。
少額のボヌナス埋め戻すための2぀の質問興味をそそる人向け
これらの質問は、むンタヌフェむスの明瀺的な実装のトピックに盎接関連しおいたせんが、ここで誰かに興味があるかもしれないように思えたす
1.リスト2の属性が別の行である堎合
 (sp as Sportsman).Run(); 

コン゜ヌルには䜕が衚瀺されたすか

2.リスト3の最小限の倉曎あるキヌワヌドを別のキヌワヌドに眮き換えるを䜿甚しお、コン゜ヌルぞの最初の質問で「私はスポヌツマンです」ずいうフレヌズを取埗するにはどうすればよいですか

Source: https://habr.com/ru/post/J156037/


All Articles