84

C#の「非信者」は、拡張メソッドの目的は何であるかを私に尋ねていました。特に、元のオブジェクトのソースを所有/制御していない場合は、すでに定義されているオブジェクトに新しいメソッドを追加できることを説明しました。

彼は「自分のクラスにメソッドを追加してみませんか?」と提起しました。私たちは(良い意味で)ぐるぐる回っています。私の一般的な反応は、それがツールベルトの別のツールであり、彼の反応はそれがツールの無駄な無駄であるということです...しかし、私はもっと「啓発された」答えが得られると思いました。

自分のクラスに追加されたメソッドを使用することができなかった(または使用すべきではなかった)拡張メソッドを使用したシナリオにはどのようなものがありますか?

4

30 に答える 30

82

拡張メソッドの唯一の利点は、コードの読みやすさです。それでおしまい。

拡張メソッドを使用すると、次のことができます。

foo.bar();

これの代わりに:

Util.bar(foo);

現在、C# にはこのようなものがたくさんあります。言い換えれば、C# には、些細に見えて、それ自体では大きな利点をもたらさない多くの機能があります。ただし、これらの機能を組み合わせ始めると、その部分の合計よりも少し大きなものが見え始めます。LINQ クエリは拡張メソッドなしではほとんど読めないため、LINQ は拡張メソッドから大きな恩恵を受けます。拡張メソッドがなくてもLINQ は可能ですが、実用的ではありません。

拡張メソッドは、C# の部分クラスによく似ています。それ自体ではあまり役に立ちませんし、取るに足らないものに思えます。しかし、生成されたコードを必要とするクラスで作業を開始すると、部分クラスの方がはるかに理にかなっています。

于 2009-01-28T15:04:36.830 に答える
35

拡張メソッドは、コードを記述するときに大いに役立つと思います。基本型に拡張メソッドを追加すると、インテリセンスですばやく取得できます。

ファイルサイズをフォーマットするフォーマットプロバイダーがあります。それを使用するには、次のように書く必要があります。

Console.WriteLine(String.Format(new FileSizeFormatProvider(), "{0:fs}", fileSize));

私が書くことができる拡張メソッドの作成:

Console.WriteLine(fileSize.ToFileSize());

よりクリーンでシンプル。

于 2009-01-28T15:07:21.927 に答える
34

工具を忘れないでください!タイプFooに拡張メソッドMを追加すると、Fooのインテリセンスリストに「M」が含まれます(拡張クラスがスコープ内にあると想定)。これにより、MyClass.M(Foo、...)よりも「M」を簡単に見つけることができます。

結局のところ、それは他の場所(静的メソッド)の単なる構文糖衣ですが、家を買うようなものです:「場所、場所、場所!」タイプに掛かっていれば、人々はそれを見つけるでしょう!

于 2009-01-28T15:01:42.313 に答える
29

私が遭遇した拡張メソッドの2つの利点:

  • 流暢なインターフェースは、拡張メソッドの静的クラスにカプセル化することができます。これにより、コア クラスとその流暢な拡張との間の懸念を分離できます。私はそれがより優れた保守性を達成するのを見てきました。
  • 拡張メソッドはインターフェイスから切り離すことができるため、(インターフェイスを介して) コントラクトを指定し、(拡張メソッドを介して) 関連する一連のインターフェイス ベースの動作を指定することができ、再び関心の分離が提供されます。例としては、 、 などの Linq 拡張メソッドがSelect(...)あります。インターフェースWhere(...)からハングアップしました。IEnumerable<T>
于 2009-01-28T15:18:03.153 に答える
26

拡張メソッドの最適な用途のいくつかは、次の機能です。

  1. サード パーティ オブジェクトの機能を拡張します (商用または自社の内部に関係なく、別のグループによって管理されます)。多くの場合、 としてマークされsealedます。
  2. 抽象クラスを実装せずにインターフェースのデフォルト機能を作成する

たとえば、IEnumerable<T>. 拡張メソッドが豊富ですが、汎用の ForEach メソッドが実装されていないのが気になりました。だから、私は自分で作った:

public void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
{
    foreach ( var o in enumerable )
    {
        action(o);
    }
}

ほら、IEnumerable<T>実装タイプに関係なく、すべてのオブジェクトが作成されたかどうかに関係なくForEach、コードに適切な「using」ステートメントを追加することにより、メソッドが作成されました。

于 2009-01-28T15:52:53.003 に答える
12

拡張メソッドを使用する大きな理由の1つは、LINQです。拡張メソッドがないと、LINQでできることの多くは非常に困難になります。Where()、Contains()、Select拡張メソッドは、構造を変更せずに既存の型にさらに多くの機能が追加されることを意味します。

于 2009-01-28T15:01:49.677 に答える
9

拡張メソッドの利点については、多くの回答があります。欠点に対処するのはどうですか?

最大の欠点は、同じコンテキストで同じシグネチャを持つ通常のメソッドと拡張メソッドがある場合に、コンパイラ エラーや警告が発生しないことです。

特定のクラスに適用される拡張メソッドを作成するとします。その後、誰かがそのクラス自体に同一の署名を持つメソッドを作成します。

コードはコンパイルされ、実行時エラーが発生することさえありません。 しかし、以前と同じコードを実行することはなくなりました。

于 2010-01-21T21:44:36.237 に答える
8

CodeBetterで Greg Young が示したFluent Interfaces and Context Sensitivity

于 2009-01-28T15:05:01.073 に答える
4

拡張メソッドで何ができるかについては、上記の素晴らしい回答が山ほどあります。

私の簡単な答えは、工場の必要性がほぼなくなるということです。

それらは新しい概念ではなく、それらの最大の検証の 1 つは、それらが Objective-C のキラー機能 (カテゴリ) であることです。フレームワークベースの開発に非常に大きな柔軟性を追加するため、NeXT には NSA とウォール街の金融モデル作成者が主要なユーザーとして含まれていました。

REALbasic はこれらをextends メソッドとしても実装しており、同様に開発を簡素化するために使用されています。

于 2009-02-05T05:15:22.830 に答える
4

拡張メソッドの背後にある重要な理由として、コードの読みやすさの向上に言及している他の回答をここでサポートしたいと思います。メソッド チェーンとネストされたメソッド呼び出しの 2 つの側面、および無意味な静的クラス名による LINQ クエリの乱雑さです。


例として、次の LINQ クエリを見てみましょう。

numbers.Where(x => x > 0).Select(x => -x)

WhereとはどちらSelectも拡張メソッドであり、 static クラス で定義されていますEnumerable。したがって、拡張メソッドが存在せず、これらが通常の静的メソッドである場合、コードの最後の行は基本的に次のようになります。

Enumerable.Select(Enumerable.Where(numbers, x => x > 0), x => -x)

そのクエリがどれほど厄介なものになったかを確認してください。


次に、独自のクエリ演算子を導入したい場合Enumerable、他のすべての標準クエリ演算子と同様に、静的クラス内でそれを定義する方法は当然ありません。これEnumerableは、フレームワーク内にあり、そのクラスを制御できないためです。したがって、拡張メソッドを含む独自の静的クラスを定義する必要があります。次に、次のようなクエリを取得する場合があります。

Enumerable.Select(MyEnumerableExtensions.RemoveNegativeNumbers(numbers), x => -x)
//                ^^^^^^^^^^^^^^^^^^^^^^
//                different class name that has zero informational value
//                and, as with 'Enumerable.xxxxxx', only obstructs the
//                query's actual meaning.
于 2010-09-15T19:21:24.927 に答える
4

拡張メソッドに関する私の個人的な主張は、それらが OOP 設計に非常によく適合するということです。単純なメソッドを検討してください。

bool empty = String.IsNullOrEmpty (myString)

と比較して

bool empty = myString.IsNullOrEmpty ();
于 2009-01-28T15:04:31.590 に答える
3

拡張メソッドによってコードの可読性が向上することには同意しますが、実際には静的ヘルパー メソッドに他なりません。

クラスに動作を追加するための拡張メソッドを使用する IMO は次のようになります。

紛らわしい: プログラマーは、メソッドが拡張型の一部であると信じている可能性があります。そのため、拡張名前空間がインポートされていないときにメソッドがなくなった理由を理解できません。

アンチパターン: 拡張メソッドを使用してフレームワークの型に動作を追加し、それをユニット テストに使用する人に渡すことにします。現在、彼は偽造できないメソッドを多数含むフレームワークに行き詰まっています。

于 2010-09-15T18:21:25.343 に答える
3

(拡張) メソッドをクラスに直接追加できることは事実です。しかし、すべてのクラスがあなたによって書かれるわけではありません。コア ライブラリまたはサード パーティ ライブラリのクラスは閉じられていることが多く、拡張メソッドなしではシンタティック シュガーを取得することは不可能です。ただし、拡張メソッドは、たとえば. c++

于 2009-01-28T15:07:37.853 に答える
2
  • 醜いユーティリティ関数を呼び出す代わりに、オブジェクト自体に対するIntellisense
  • 変換関数の場合、「XToY(X x)」を「ToY(this X x)」に変更できます。これにより、醜いXToY(x)ではなくかなりのx.ToY()になります。
  • 制御できないクラスを拡張する
  • クラス自体にメソッドを追加することが望ましくない場合は、クラスの機能を拡張します。たとえば、ビジネスオブジェクトをシンプルでロジックフリーに保ち、拡張メソッドに醜い依存関係を持つ特定のビジネスロジックを追加できます。
于 2009-02-01T04:45:28.897 に答える
2

それらを使用して、オブジェクト モデル クラスを再利用します。データベースにあるオブジェクトを表すクラスがたくさんあります。これらのクラスはクライアント側でオブジェクトを表示するためにのみ使用されるため、基本的な使用法はプロパティへのアクセスです。

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 定義ファイルを読み取り、データベースにあるオブジェクトを表すこれらのスタブ クラスを生成するコンパイラがあります。この場合の唯一のアプローチは、それらを拡張することです。

于 2009-02-05T04:19:59.307 に答える
2

拡張メソッドは、実際には、Martin Fowler's Book の「Introduce Foreign Method」リファクタリング (メソッド シグネチャに至るまで) を.NET に組み込んだものです。基本的に同じ利点と落とし穴があります。このリファクタリングのセクションで、彼は、メソッドを実際に所有するクラスを変更できない場合の回避策であると述べています。

于 2009-01-28T15:08:43.217 に答える
2

私は主に、拡張メソッドを、無料の関数を禁止するべきではなかったという承認として見ています。

C++ コミュニティでは、多くの場合、メンバーよりもフリーの非メンバー関数を優先することが OOP の適切なプラクティスと見なされています。これらの関数は、必要のないプライベート メンバーにアクセスすることによってカプセル化を破らないためです。拡張メソッドは、同じことを達成するための回り道のようです。つまり、プライベート メンバーにアクセスできない静的関数のよりクリーンな構文です。

拡張メソッドはシンタックス シュガーに過ぎませんが、それらを使用しても問題はないと思います。

于 2009-01-28T15:34:22.917 に答える
1

前回のプロジェクトでは、拡張メソッドを使用してValidate()メソッドをビジネスオブジェクトにアタッチしました。これを正当化したのは、シリアル化可能なデータがオブジェクトを転送し、製品、顧客、商人などの一般的なeコマースエンティティが異なるドメインで使用されるビジネスオブジェクトです。異なるドメインでは、ビジネスルールも異なる可能性があるため、カプセル化しました。データ転送オブジェクトの基本クラスに関連付けられたValidateメソッドのレイトバウンド検証ロジック。これが理にかなっていることを願っています:)

于 2009-01-28T15:26:36.660 に答える
1

これにより、C# は動的言語、LINQ、その他多数の機能をより適切にサポートできるようになります。Scott Guthrie の記事をご覧ください。

于 2009-01-28T15:06:09.917 に答える
1

拡張メソッドを使用して、C# で一種の mixinを作成できます。

これにより、直交する概念の懸念事項をより適切に分離できます。例として、この回答を見てください。

これは、 DCI アーキテクチャの中心となる概念である C# でロールを有効にするためにも使用できます。

于 2010-09-15T18:25:51.740 に答える
1

また、拡張メソッドは、Linq クエリを C# スタイルで使用したときに読みやすくするために追加されたことを思い出してください。

これらの 2 つの影響は完全に同等ですが、最初の影響ははるかに読みやすくなっています (もちろん、チェーンされたメソッドが増えると、読みやすさのギャップが大きくなります)。

int n1 = new List<int> {1,2,3}.Where(i => i % 2 != 0).Last();

int n2 = Enumerable.Last(Enumerable.Where(new List<int> {1,2,3}, i => i % 2 != 0));

完全修飾構文は次のようにする必要があることに注意してください。

int n1 = new List<int> {1,2,3}.Where<int>(i => i % 2 != 0).Last<int>();

int n2 = Enumerable.Last<int>(Enumerable.Where<int>(new List<int> {1,2,3}, i => i % 2 != 0));

偶然にも、 および の型パラメーターは、これら 2 つのメソッドの最初のパラメーター (キーワードによって導入され、それらを拡張メソッドにするパラメーター) の存在のおかげで推論できるため、明示的に言及する必要はWhereありません。Lastthis

この点は明らかに拡張メソッドの利点であり、メソッド チェーンが関係するすべての同様のシナリオでその利点を活かすことができます。

特に、任意のサブクラスから呼び出し可能な基本クラス メソッドを持ち、このサブクラスへの厳密に型指定された参照を (サブクラス型で) 返すことがわかった、より洗練された説得力のある方法です。

例(わかりました、このシナリオは完全に安っぽいです):おやすみの後、動物が目を開けて泣きます。どの動物も同じように目を開けますが、犬は吠え、アヒルは鳴き声を上げます。

public abstract class Animal
{
    //some code common to all animals
}

public static class AnimalExtension
{
    public static TAnimal OpenTheEyes<TAnimal>(this TAnimal animal) where TAnimal : Animal
    {
        //Some code to flutter one's eyelashes and then open wide
        return animal; //returning a self reference to allow method chaining
    }
}

public class Dog : Animal
{
    public void Bark() { /* ... */ }
}

public class Duck : Animal
{
    public void Kwak() { /* ... */ }
}

class Program
{
    static void Main(string[] args)
    {
        Dog Goofy = new Dog();
        Duck Donald = new Duck();
        Goofy.OpenTheEyes().Bark(); //*1
        Donald.OpenTheEyes().Kwak(); //*2
    }
}

概念的にはメソッドでOpenTheEyesある必要がありAnimalますが、抽象クラスのインスタンスを返します。これは、やなどAnimalの特定のサブクラスメソッドを認識していません。*1 と *2 としてコメントされた 2 行は、コンパイル エラーを発生させます。BarkDuck

しかし、拡張メソッドのおかげで、「呼び出されたサブクラスの型を知っている基本メソッド」のようなものを持つことができます。

単純なジェネリック メソッドで作業を完了できた可能性がありますが、はるかに厄介な方法であることに注意してください。

public abstract class Animal
{
    //some code common to all animals

    public TAnimal OpenTheEyes<TAnimal>() where TAnimal : Animal
    {
        //Some code to flutter one's eyelashes and then open wide
        return (TAnimal)this; //returning a self reference to allow method chaining
    }
}

今回は、パラメーターがないため、戻り値の型を推測することはできません。呼び出しは次のとおりです。

Goofy.OpenTheEyes<Dog>().Bark();
Donald.OpenTheEyes<Duck>().Kwak();

...より多くのチェーンが含まれる場合、コードの重みが大きくなる可能性があります(特に、型パラメーターが常に<Dog>グーフィーの行と<Duck>ドナルドの行にあることを知っている...)

于 2011-01-15T21:01:12.110 に答える
0

私はそれについて一言だけ言います:保守性これは拡張メソッドが使用するための鍵です

于 2009-01-28T14:58:43.503 に答える
0

拡張メソッドは、ネストされたジェネリック引数に一致させるのに役立つことがわかりました。

それは少し奇妙に聞こえますが、ジェネリック classMyGenericClass<TList>があり、TList 自体がジェネリック (たとえば a ) であることを知っているとList<T>します。ネストされた 'T' を拡張なしで List から掘り出す方法はないと思います。メソッドまたは静的ヘルパー メソッド。自由に使用できる静的ヘルパー メソッドしかない場合、(a) 醜く、(b) クラスに属する機能を外部の場所に移動する必要があります。

たとえば、タプルの型を取得し、それらをメソッド シグネチャに変換するには、拡張メソッドを使用できます。

public class Tuple { }
public class Tuple<T0> : Tuple { }
public class Tuple<T0, T1> : Tuple<T0> { }

public class Caller<TTuple> where TTuple : Tuple { /* ... */ }

public static class CallerExtensions
{
     public static void Call<T0>(this Caller<Tuple<T0>> caller, T0 p0) { /* ... */ }

     public static void Call<T0, T1>(this Caller<Tuple<T0, T1>> caller, T0 p0, T1 p1) { /* ... */ }
}

new Caller<Tuple<int>>().Call(10);
new Caller<Tuple<string, int>>().Call("Hello", 10);

とはいえ、境界線がどこにあるべきかわかりません-メソッドが拡張メソッドであるべき場合と、静的ヘルパーメソッドであるべき場合はいつですか? 何かご意見は?

于 2010-01-21T21:29:25.653 に答える
0

HTMLを構築するためにそれらが大好きです。多くの場合、繰り返し使用されるセクション、または関数が有用であるがそうでなければプログラムの流れを壊す場所で再帰的に生成されるセクションがあります。

        HTML_Out.Append("<ul>");
        foreach (var i in items)
            if (i.Description != "")
            {
                HTML_Out.Append("<li>")
                    .AppendAnchor(new string[]{ urlRoot, i.Description_Norm }, i.Description)
                    .Append("<div>")
                    .AppendImage(iconDir, i.Icon, i.Description)
                    .Append(i.Categories.ToHTML(i.Description_Norm, urlRoot)).Append("</div></li>");
            }

        return HTML_Out.Append("</ul>").ToString();

オブジェクトが HTML 出力用にカスタム ロジックを準備する必要がある場合もあります。拡張メソッドを使用すると、クラス内でプレゼンテーションとロジックを混在させることなく、この機能を追加できます。

于 2009-09-19T21:28:10.797 に答える
0

拡張メソッドの素晴らしい例がたくさんあります。特に上記の IEnumerables についてです。

たとえば、IEnumerable<myObject>作成できる拡張メソッドがある場合IEnumerable<myObject>

mylist List<myObject>;

...リストを作成する

mylist.DisplayInMyWay();

拡張メソッドがなければ、次を呼び出す必要があります。

myDisplayMethod(myOldArray); // can create more nexted brackets.

もう 1 つの優れた例は、循環リンク リストを瞬時に作成することです。

私はそれを信用することはできません!

拡張メソッドを使用した循環リンク リスト

これらを組み合わせて拡張メソッドを使用すると、コードは次のようになります。

myNode.NextOrFirst().DisplayInMyWay();

それよりも

DisplayInMyWay(NextOrFirst(myNode)).

拡張メソッドを使用すると、よりすっきりと読みやすくなり、よりオブジェクト指向になります。にも非常に近い:

myNode.Next.DoSomething()

それを同僚に見せてください!:)

于 2012-04-28T09:27:20.900 に答える
0

画面に入力ゾーンがあり、正確なタイプ(テキストボックス、チェックボックスなど)が何であれ、すべてが標準の動作を実装する必要があります。各タイプの入力ゾーンはすでに特定のクラス (TextInputBox など) から派生しているため、共通の基本クラスを継承することはできません。

おそらく、継承階層を上に行くことで、WebControl などの共通の祖先を見つけることができますが、フレームワーク クラス WebControl を開発していないため、必要なものが公開されていません。

拡張メソッドを使用すると、次のことができます。

1) WebControl クラスを拡張し、すべての入力クラスで統一された標準動作を取得する

2)代わりに、すべてのクラスをインターフェイスから派生させ、たとえば IInputZone を作成し、このインターフェイスをメソッドで拡張します。これで、すべての入力ゾーンでインターフェイスに関連する拡張メソッドを呼び出すことができるようになります。入力ゾーンがすでに複数の基本クラスから派生しているため、このようにして一種の多重継承を実現しました。

于 2010-12-04T12:23:45.807 に答える
0

これにより、エディター/IDE がスマートにオートコンプリートの提案を行うことができます。

于 2009-02-05T05:43:32.773 に答える
0

拡張メソッドは、より明確なコードを書くのに役立つと思います。

友人が提案したように、クラス内に新しいメソッドを配置する代わりに、ExtensionMethods 名前空間に配置します。このようにして、クラスの論理的な秩序を維持します。クラスを実際に直接処理しないメソッドは、クラスを乱雑にすることはありません。

拡張メソッドを使用すると、コードがより明確になり、より適切に整理されると思います。

于 2009-01-28T16:23:34.753 に答える