これらの質問は、拡張メソッドについて以前に尋ねた質問のフォローアップです。
10 に答える
拡張メソッドを使用する意味があるのはいつですか?
これらは、LINQを使用していて、関数出力をある関数から別の関数にチェーンまたはパイプしたい場合に意味があります。これにより、コードの可読性が向上し、概念をよりエレガントに表現できるようになります(価値のあるものは何でも)。
また、そのタイプのソースを変更せずに、任意のタイプのインスタンスメソッドの外観を与えることができます。これは、コードが合理的に使用されている場合に、コードの可読性と表現力を高めるのに役立ちます。
タイプに拡張メソッドを追加すると、パフォーマンスに影響しますか?
次のような拡張メソッド呼び出しに注意してください。
instance.SomeExtensionMethod()
コンパイルされます:
StaticExtensionMethodClass.SomeExtensionMethod(instance);
したがって、パフォーマンスは他の静的メソッド呼び出しと同じになります。
ここでの私の答えから:
拡張メソッドの実際の使用に関しては、新しいクラスを派生させずに、クラスに新しいメソッドを追加することができます。
次の例を見てください。
public class extended {
public int sum() {
return 7+3+2;
}
}
public static class extending {
public static float average(this extended extnd) {
return extnd.sum() / 3;
}
}
ご覧のとおり、クラスExtending
はaverageという名前のメソッドをクラスに追加していExtended
ます。平均を取得するには、クラスaverage
に属しているため、メソッドを呼び出します。extended
extended ex = new extended();
Console.WriteLine(ex.average());
パフォーマンスに関しては、拡張メソッドが動的にディスパッチされることはないため、改善が見られると思いますが、それはすべて動的メソッドの実装方法に依存します。
その蜂は、実際にそれらを変更することなく、既存のクラスの機能を拡張(追加)するために使用されました。
これは、LINQ(System.Linq名前空間など)がすべてのコレクションに多くの機能を追加する方法で確認できます。
拡張メソッドは、関数型プログラミングが使用される場合に役立ちます。
アプリにすでにあるほぼすべてのモジュール レベルの関数と、それらを拡張メソッドとしてタグ付けするとどうなるかを検討してください。それらは「受動態」を超えた「能動態」の機会となります。アクティブ ボイスとは、インスタンスに対して受動的にアクションを実行させるのではなく、インスタンスが特定のタスクを実行するための独自のメソッドを提供しているかのようにコードが読み取られることを意味します。
ExtendUnlimitedCredit(AddVIP(tblCustomer, "Bill Gates"))
対。
tblCustomer.AddVIP("Bill Gates").ExtendUnlimitedCredit()
拡張メソッドを使用すると、この変換が簡単になります。「アクティブな音声」コードを使用してネストされた関数呼び出しを排除すると、多くの場合改善されます。さらに、私たちのショップはコア クラスをロックダウンしているため、拡張メソッドの前にネストされた関数呼び出ししか利用できませんでした。
拡張メソッドに関するその他の優れた点:
それらは (Intellisense のため) 関数をより見つけやすくします。また、関数の目的と使用法を説明するインライン マークアップを提供した場合、Intellisense はメソッドとその使用法を説明する便利なツールチップを、それを発見した開発者に提供します (単にドットを押すだけです)。拡張メソッドとしてタグ付けされていない関数は、それほど簡単には発見されず、使用されない可能性があり、その結果、他の誰かがその関数の独自のフレーバーを発明する可能性があります。
インターフェイスのメソッドを実際に実装することはできませんが、拡張メソッドは実装したように見せる代替手段を提供します。
拡張メソッドのもう 1 つの興味深い使用法は、特定の機能をある名前空間のクラスに追加し、別の名前空間には追加しない場合です。具体的な例の 1 つは、単体テストを容易にするメソッドを追加することです。メソッドによって実稼働アセンブリが乱雑になることは望ましくありませんが、単体テストを作成する場合に便利です。
他の回答に加えて、拡張メソッドは定型的な実装をインターフェイスに追加する優れた方法です。たとえば、すべてのリストを並べ替え可能にしたい場合は、 の拡張メソッドを追加しIList<T>
ます。
(既に述べたように) 拡張メソッドを使用して、コントロール外のクラスにメソッドを追加することもできます。Reverse()
のメソッドが欲しかったことはありstring
ますか? 1つ追加してください!
唯一の違いは、拡張メソッドが virtual を使用せず、null チェックがないことです。必要に応じて、これを有利に使用できます。
public static void ThrowIfNull<T>(this T obj, string name) where T : class
{
if(obj == null) throw new ArgumentNullException(name);
}
通常のユーティリティ メソッドとは異なり、流暢なインターフェイスを非常に簡単に記述できます。これは、それらが存在する理由の 1 つです。つまり、LINQ の場合です。
var foo = source.Where(predicate).OrderBy(selector);
次のものよりもはるかに読みやすいです。
var foo = Enumerable.OrderBy(Enumerable.Where(source,predicate),selector);
通常のメソッドでは、最初のアプローチを使用するには通常のインスタンス メソッドを使用する必要があり、(たとえば) IEnumerable<T>
- への変更が必要になります。これは望ましくありません。
パフォーマンスへの影響はわかりません。拡張メソッドの使用は、ソースコードにアクセスできないため、メソッドをクラスに直接追加できない場合に最も意味があり、メソッドは関数として実装するのが理にかなっています。これは、前の質問で私が行ったコメントに当てはまります。他の1人が、文字列が有効な電子メールであるかどうかに基づいてブール値を返す'string'クラスの拡張メソッドのサンプルを提供しました。これ、IMOは、拡張メソッドを使用しない場合の例です。これは、その関数が文字列型の基本ではないためです。ただし、Left(int)関数とRight(int)関数を'string'に追加することは理にかなっています。
それらを使用して、オブジェクト モデル クラスを再利用します。データベースにあるオブジェクトを表すクラスがたくさんあります。これらのクラスは、オブジェクトを表示するためにクライアント側でのみ使用されるため、基本的な使用法はプロパティへのアクセスです。
public class Stock {
public Code { get; private set; }
public Name { get; private set; }
}
その使用パターンのため、これらのクラスにビジネス ロジック メソッドを含めたくないので、すべてのビジネス ロジックを拡張メソッドにしています。
public static class StockExtender {
public static List <Quote> GetQuotesByDate(this Stock s, DateTime date)
{...}
}
このようにして、不要なコードでクライアント側をオーバーロードすることなく、ビジネス ロジックの処理とユーザー インターフェイスの表示に同じクラスを使用できます。
このソリューションの興味深い点の 1 つは、私のオブジェクト モデル クラスがMono.Cecilを使用して動的に生成されることです。そのため、必要に応じてビジネス ロジック メソッドを追加することは非常に困難です。XML 定義ファイルを読み取り、データベースにあるオブジェクトを表すこれらのスタブ クラスを生成するコンパイラがあります。この場合の唯一のアプローチは、それらを拡張することです。
2 番目の質問への対処: 私の経験則では、拡張メソッドは型の「機能の自然な拡張」である必要があるか、流暢なインターフェイスの保守可能で読み取り可能な部分である必要があります。
「機能の自然な拡張」の例は、DBNull が呼び出された場合にデフォルト値を返すようにデータ リーダーを拡張することです。複数のフィールドのデータによって表されるエンティティのインスタンスを返すようにデータ リーダーを拡張することは、それほど自然なことではありません。後者の場合、不適切なオブジェクトに間違った責任を注入しています:)。