web-dev-qa-db-ja.com

C#拡張メソッドの設計パターンと使用ガイドライン?

C#拡張メソッドは、近年使用が増加しています。使用状況に関するマイクロソフトの公式ガイドライン:「一般的に、拡張メソッドは慎重に実装し、必要な場合にのみ実装することをお勧めします」 拡張メソッドのガイドライン

一方、Microsoftはそれらをドットネットコアで頻繁に使用しています(Microsoft拡張機能の名前空間を参照)。これは特に普及しているaspコアで、IServiceCollectionの初期化が拡張メソッドで実装されています。たとえば、 サービスコレクションサービス拡張 を参照してください。 Dot netコアチュートリアルには、これを design pattern としてリストする記事があり、ベストプラクティスであることを示唆しています。かなりの数の一般的なnugetパッケージもこのメソッドを使用してサービスを初期化します。Swaggerはこのように構成されます Microsoft docs およびmediatr その実装

ガイドラインを更新する必要がありますか、それともこのような行為は避けるべきですか?ガイドラインを更新する場合、何をすべきですか?

2
Adrian

拡張メソッドは、通常の静的メソッド呼び出しの単なる構文糖衣です。

拡張メソッドは、継承、合成、ウィービング、またはその他の言語メカニズムを必要とせずに、メソッドを既存の型に「スポット溶接」する機能を可能にします。ただし、これらは通常のメソッド呼び出しのプロキシにすぎません。クラスの設計には参加せず、クラスにまだない新しい機能を追加することもありません。

拡張メソッドが行うことはこれを有効にすることです:

Manager.Foo(bar);

これに:

bar.Foo();

そして、それが実際に行うことのすべてです。

それは間違いなくきれいです(これは小さなことではありません)が、実際には、拡張メソッドを使用する他の説得力のある理由はありますか?

はい:メソッドチェーンを有効にします。

var productList = products
   .Where(prod => prod.StateOfManufacture = "CA")
   .OrderBy(prod => prod.Category)
   .ThenByDescending(prod => prod.UnitPrice)
   .Select(prod => new 
        { Name = prod.ProductName, Category = prod.Category, Price = prod.UnitPrice });

これは、拡張メソッドなしでは非常に退屈です。

アドバイス "拡張メソッドは慎重に使用し、必要な場合にのみ使用してください"は実行できません。全体 librariesframeworks は拡張メソッドを使用して構築されており、あなたが「する必要はありません」

それは「ベストプラクティス」ですか。それに対する私の答えは、「それはあなたの特定のニーズを最も効果的に満たしていますか?」です。

5
Robert Harvey

拡張メソッドの目的は、具体的には他のアセンブリまたはインターフェイスのクラスを拡張できるようにすることです。それらを控えめに使用することの推奨は、いくつかの理由で正しいです:

  • 拡張メソッドは、使用しすぎるとIntelliSenseを汚染します。これは、(1)拡張メソッドがプロジェクトのほぼすべてのファイルにusing- includedされているコア名前空間で作成され、(2)それらがいくつかの非常に一般的なインターフェースまたはクラスを拡張している場合、非常に煩わしい可能性があります。 、objectなど。

    そのような汚染の例はNSubstituteです。 IntelliSenseは、NSubstituteを含むテストプロジェクトではほとんど役に立たなくなります。これは、すべてに対して自動的に提案される非常に多くのアイテムがあるため、特定のクラスまたはインターフェイスのメンバーを知る唯一の方法は、そのドキュメントを参照することです。少しがっかり。

  • 拡張メソッドを乱用する人は、多くの場合初等継承を使用できます。より難解な状況では、 デコレータパターン

    私が最近見た基本的な例。コードは幾何学的形状を処理しており、とりわけメートルからインチに変換する必要がありました。開発者は、2つの拡張メソッド、ToInchesおよびToMetersintに、他の2つの拡張メソッドをdoubleに配置するだけです。代わりに、使用されているメトリックシステムを含む専用クラスを作成し、他のシステムへの変換を可能にします。

  • 拡張メソッドは、任意のパブリック静的メソッドと同様に、ユニットテストを必要以上に難しくします。これは、で注入されたオブジェクトのインスタンスメソッドとは対照的ですランタイム。モックまたはスタブで置き換えることができます。

  • あなたに属していないコードを拡張することにより、あなたは自分の拡張メソッドと同じ名前を持つメソッドを追加するコードの作者の危険にさらされます、ただし、実装が異なります。これだけでも、拡張メソッドの使用は控えめにするか、少なくともインターフェースのみに制限することをお勧めします。

    例として、数年前、開発者仲間が私に話をしました。彼らのコードベースにはpublic static string ToJson(this object x)と呼ばれる魔法のメソッドがあり、ご想像のとおり、オブジェクトのJSON表現を返します。サードパーティのクラスの1つがインスタンスToJsonメソッドを追加して更新されるまでは、すばらしいことでした。一部のオブジェクトのシリアル化ルールが突然変更され、サービスを消費していたアプリケーションが壊れる理由を解明するには、チームが約1週間かかりました。

拡張メソッドが非常に貴重な場合があります。 LINQはそのような場合です。ただし、開発者は、できるだけ多くの拡張メソッドを作成するのではなく、LINQと同様のケースに限定するように注意する必要があります。

4