拡張メソッド (C# プログラミング ガイド)Extension Methods (C# Programming Guide)

拡張メソッドを使用すると、新規の派生型の作成、再コンパイル、または元の型の変更を行うことなく既存の型にメソッドを "追加" できます。Extension methods enable you to "add" methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type. 拡張メソッドは静的メソッドですが、拡張された型のインスタンス メソッドのように呼び出します。Extension methods are static methods, but they're called as if they were instance methods on the extended type. C#、F#、Visual Basic で作成されたクライアント コードの場合は、拡張メソッドの呼び出しと、型で定義されているメソッドの呼び出しに明確な違いはありません。For client code written in C#, F# and Visual Basic, there's no apparent difference between calling an extension method and the methods defined in a type.

最も一般的な拡張メソッドは、既存の System.Collections.IEnumerable 型および System.Collections.Generic.IEnumerable<T> 型にクエリ機能を追加する LINQ 標準クエリ演算子です。The most common extension methods are the LINQ standard query operators that add query functionality to the existing System.Collections.IEnumerable and System.Collections.Generic.IEnumerable<T> types. この標準クエリ演算子を使用するには、まず using System.Linq ディレクティブを使用して、スコープに含めます。To use the standard query operators, first bring them into scope with a using System.Linq directive. IEnumerable<T> を実装するすべての型は、GroupByOrderByAverage などのインスタンス メソッドを持っていると考えられます。Then any type that implements IEnumerable<T> appears to have instance methods such as GroupBy, OrderBy, Average, and so on. List<T>Array などの IEnumerable<T> 型のインスタンスの後に "ドット" を入力すると、IntelliSense により、ステートメントの入力候補としてこれらの追加メソッドが表示されます。You can see these additional methods in IntelliSense statement completion when you type "dot" after an instance of an IEnumerable<T> type such as List<T> or Array.

OrderBy の例OrderBy Example

整数の配列において、標準クエリ演算子の OrderBy メソッドを呼び出す方法を次の例に示します。The following example shows how to call the standard query operator OrderBy method on an array of integers. かっこ内の式はラムダ式です。The expression in parentheses is a lambda expression. 標準クエリ演算子の多くはパラメーターとしてラムダ式を受け取りますが、拡張メソッドでは、これは必須ではありません。Many standard query operators take lambda expressions as parameters, but this isn't a requirement for extension methods. 詳細については、「ラムダ式」を参照してください。For more information, see Lambda Expressions.

class ExtensionMethods2
{

    static void Main()
    {
        int[] ints = { 10, 45, 15, 39, 21, 26 };
        var result = ints.OrderBy(g => g);
        foreach (var i in result)
        {
            System.Console.Write(i + " ");
        }
    }
}
//Output: 10 15 21 26 39 45

拡張メソッドは、静的メソッドとして定義しますが、インスタンス メソッドの構文を使用して呼び出します。Extension methods are defined as static methods but are called by using instance method syntax. 最初のパラメーターでは、メソッドによって操作される型を指定します。Their first parameter specifies which type the method operates on. このパラメーターの前には this 修飾子を付けます。The parameter is preceded by the this modifier. using ディレクティブを使用して名前空間をソース コードに明示的にインポートした場合、拡張メソッドはそのスコープでのみ有効です。Extension methods are only in scope when you explicitly import the namespace into your source code with a using directive.

System.String クラスに対して拡張メソッドを定義する例を次に示します。The following example shows an extension method defined for the System.String class. 入れ子になっていない、非ジェネリックの静的クラス内で定義されています。It's defined inside a non-nested, non-generic static class:

namespace ExtensionMethods
{
    public static class MyExtensions
    {
        public static int WordCount(this String str)
        {
            return str.Split(new char[] { ' ', '.', '?' },
                             StringSplitOptions.RemoveEmptyEntries).Length;
        }
    }
}

この WordCount ディレクティブを使用することで、using 拡張メソッドをスコープに取り込むことができます。The WordCount extension method can be brought into scope with this using directive:

using ExtensionMethods;

また、この構文を使用することで、アプリケーションから呼び出すことができます。And it can be called from an application by using this syntax:

string s = "Hello Extension Methods";
int i = s.WordCount();

コードでは、インスタンス メソッドの構文を使用して、拡張メソッドを呼び出します。You invoke the extension method in your code with instance method syntax. コンパイラによって生成される中間言語 (IL) により、コードは静的メソッドの呼び出しに変換されます。The intermediate language (IL) generated by the compiler translates your code into a call on the static method. カプセル化の原則には実質的に違反していません。The principle of encapsulation is not really being violated. 拡張メソッドでは、それが拡張している型のプライベート変数にはアクセスできません。Extension methods cannot access private variables in the type they are extending.

詳細については、「カスタム拡張メソッドを実装して呼び出す方法」を参照してください。For more information, see How to implement and call a custom extension method.

一般に、独自に実装するより、拡張メソッドを呼び出す方がはるかに多くなると思われます。In general, you'll probably be calling extension methods far more often than implementing your own. 拡張メソッドは、インスタンス メソッドの構文を使用して呼び出すので、特別な知識がなくてもクライアント コードからそれらを使用できます。Because extension methods are called by using instance method syntax, no special knowledge is required to use them from client code. メソッドが定義されている名前空間に関する using ディレクティブを追加するだけで、特定の型の拡張メソッドを使用できるようになります。To enable extension methods for a particular type, just add a using directive for the namespace in which the methods are defined. たとえば、標準クエリ演算子を使用するには、次の using ディレクティブをコードに追加します。For example, to use the standard query operators, add this using directive to your code:

using System.Linq;

場合によっては、System.Core.dll への参照も追加する必要があります。ほとんどの IEnumerable<T> 型で利用できる追加メソッドとして、標準クエリ演算子が IntelliSense に表示されるようになりました。(You may also have to add a reference to System.Core.dll.) You'll notice that the standard query operators now appear in IntelliSense as additional methods available for most IEnumerable<T> types.

コンパイル時の拡張メソッドのバインディングBinding Extension Methods at Compile Time

拡張メソッドを使用してクラスまたはインターフェイスを拡張することはできますが、これらをオーバーライドすることはできません。You can use extension methods to extend a class or interface, but not to override them. インターフェイス メソッドまたはクラス メソッドと同じ名前およびシグネチャを持つ拡張メソッドは決して呼び出されません。An extension method with the same name and signature as an interface or class method will never be called. コンパイル時に、型自体で定義されているインスタンス メソッドよりも低い優先順位が拡張メソッドには必ず設定されます。At compile time, extension methods always have lower priority than instance methods defined in the type itself. つまり、型に Process(int i) という名前のメソッドがあり、これと同じシグネチャの拡張メソッドがある場合、コンパイラは必ずインスタンス メソッドにバインドします。In other words, if a type has a method named Process(int i), and you have an extension method with the same signature, the compiler will always bind to the instance method. コンパイラは、メソッド呼び出しを検出すると、最初に型のインスタンス メソッドから一致するものを探します。When the compiler encounters a method invocation, it first looks for a match in the type's instance methods. 一致するものが見つからない場合、型に対して定義されている拡張メソッドを検索し、見つかった最初の拡張メソッドにバインドします。If no match is found, it will search for any extension methods that are defined for the type, and bind to the first extension method that it finds. 次の例は、コンパイラが拡張メソッドとインスタンス メソッドのどちらにバインドするかを決定する方法を示しています。The following example demonstrates how the compiler determines which extension method or instance method to bind to.

Example

次の例は、C# のコンパイラがメソッド呼び出しを型のインスタンス メソッドにバインドするか、拡張メソッドにバインドするかを決定するときに従う規則を示しています。The following example demonstrates the rules that the C# compiler follows in determining whether to bind a method call to an instance method on the type, or to an extension method. Extensions 静的クラスには、IMyInterface を実装する型に対して定義された拡張メソッドが含まれています。The static class Extensions contains extension methods defined for any type that implements IMyInterface. AB、および C の各クラスはすべてこのインターフェイスを実装しています。Classes A, B, and C all implement the interface.

MethodB 拡張メソッドは、その名前とシグネチャがクラスにより既に実装されているメソッドと完全に一致しているため、呼び出されることはありません。The MethodB extension method is never called because its name and signature exactly match methods already implemented by the classes.

コンパイラは、一致するシグネチャを持つインスタンス メソッドを検出できない場合、一致する拡張メソッドが存在する場合はそれにバインドします。When the compiler can't find an instance method with a matching signature, it will bind to a matching extension method if one exists.

// Define an interface named IMyInterface.
namespace DefineIMyInterface
{
    using System;

    public interface IMyInterface
    {
        // Any class that implements IMyInterface must define a method
        // that matches the following signature.
        void MethodB();
    }
}

// Define extension methods for IMyInterface.
namespace Extensions
{
    using System;
    using DefineIMyInterface;

    // The following extension methods can be accessed by instances of any
    // class that implements IMyInterface.
    public static class Extension
    {
        public static void MethodA(this IMyInterface myInterface, int i)
        {
            Console.WriteLine
                ("Extension.MethodA(this IMyInterface myInterface, int i)");
        }

        public static void MethodA(this IMyInterface myInterface, string s)
        {
            Console.WriteLine
                ("Extension.MethodA(this IMyInterface myInterface, string s)");
        }

        // This method is never called in ExtensionMethodsDemo1, because each
        // of the three classes A, B, and C implements a method named MethodB
        // that has a matching signature.
        public static void MethodB(this IMyInterface myInterface)
        {
            Console.WriteLine
                ("Extension.MethodB(this IMyInterface myInterface)");
        }
    }
}

// Define three classes that implement IMyInterface, and then use them to test
// the extension methods.
namespace ExtensionMethodsDemo1
{
    using System;
    using Extensions;
    using DefineIMyInterface;

    class A : IMyInterface
    {
        public void MethodB() { Console.WriteLine("A.MethodB()"); }
    }

    class B : IMyInterface
    {
        public void MethodB() { Console.WriteLine("B.MethodB()"); }
        public void MethodA(int i) { Console.WriteLine("B.MethodA(int i)"); }
    }

    class C : IMyInterface
    {
        public void MethodB() { Console.WriteLine("C.MethodB()"); }
        public void MethodA(object obj)
        {
            Console.WriteLine("C.MethodA(object obj)");
        }
    }

    class ExtMethodDemo
    {
        static void Main(string[] args)
        {
            // Declare an instance of class A, class B, and class C.
            A a = new A();
            B b = new B();
            C c = new C();

            // For a, b, and c, call the following methods:
            //      -- MethodA with an int argument
            //      -- MethodA with a string argument
            //      -- MethodB with no argument.

            // A contains no MethodA, so each call to MethodA resolves to
            // the extension method that has a matching signature.
            a.MethodA(1);           // Extension.MethodA(IMyInterface, int)
            a.MethodA("hello");     // Extension.MethodA(IMyInterface, string)

            // A has a method that matches the signature of the following call
            // to MethodB.
            a.MethodB();            // A.MethodB()

            // B has methods that match the signatures of the following
            // method calls.
            b.MethodA(1);           // B.MethodA(int)
            b.MethodB();            // B.MethodB()

            // B has no matching method for the following call, but
            // class Extension does.
            b.MethodA("hello");     // Extension.MethodA(IMyInterface, string)

            // C contains an instance method that matches each of the following
            // method calls.
            c.MethodA(1);           // C.MethodA(object)
            c.MethodA("hello");     // C.MethodA(object)
            c.MethodB();            // C.MethodB()
        }
    }
}
/* Output:
    Extension.MethodA(this IMyInterface myInterface, int i)
    Extension.MethodA(this IMyInterface myInterface, string s)
    A.MethodB()
    B.MethodA(int i)
    B.MethodB()
    Extension.MethodA(this IMyInterface myInterface, string s)
    C.MethodA(object obj)
    C.MethodA(object obj)
    C.MethodB()
 */

一般的な使用パターンCommon Usage Patterns

コレクションの機能Collection Functionality

これまでは、特定の型の System.Collections.Generic.IEnumerable<T> インターフェイスが実装され、その型のコレクションに対して動作する機能が含まれている、"コレクション クラス" を作成するのが一般的でした。In the past, it was common to create "Collection Classes" that implemented the System.Collections.Generic.IEnumerable<T> interface for a given type and contained functionality that acted on collections of that type. この種類のコレクション オブジェクトを作成しても問題はありませんが、System.Collections.Generic.IEnumerable<T> の拡張機能を使用して同じ機能を実現できます。While there's nothing wrong with creating this type of collection object, the same functionality can be achieved by using an extension on the System.Collections.Generic.IEnumerable<T>. 拡張機能には、その型の System.Collections.Generic.IEnumerable<T> が実装されている System.ArraySystem.Collections.Generic.List<T> などの任意のコレクションから機能を呼び出すことができるという利点があります。Extensions have the advantage of allowing the functionality to be called from any collection such as an System.Array or System.Collections.Generic.List<T> that implements System.Collections.Generic.IEnumerable<T> on that type. Int32 の配列のこの使用例については、この記事で既に示されています。An example of this using an Array of Int32 can be found earlier in this article.

レイヤー固有の機能Layer-Specific Functionality

オニオン アーキテクチャまたは他のレイヤー化アプリケーション設計を使用する場合は、アプリケーションの境界を越えて通信するために使用できるドメイン エンティティまたはデータ転送オブジェクトのセットを使用するのが一般的です。When using an Onion Architecture or other layered application design, it's common to have a set of Domain Entities or Data Transfer Objects that can be used to communicate across application boundaries. これらのオブジェクトには、通常、機能が含まれていないか、またはアプリケーションのすべてのレイヤーに適用される最小限の機能のみが含まれています。These objects generally contain no functionality, or only minimal functionality that applies to all layers of the application. 他のレイヤーで必要のないメソッドや望ましくないメソッドを使用してオブジェクトを読み込むことなしに、拡張メソッドを使用して、各アプリケーション レイヤーに固有の機能を追加することができます。Extension methods can be used to add functionality that is specific to each application layer without loading the object down with methods not needed or wanted in other layers.

public class DomainEntity
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

static class DomainEntityExtensions
{
    static string FullName(this DomainEntity value)
        => $"{value.FirstName} {value.LastName}";
}

定義済みの型の拡張Extending Predefined Types

再利用可能な機能を作成する必要があるときに、新しいオブジェクトを作成するのではなく、多くの場合、.NET Framework や CLR 型などの既存の型を拡張できます。Rather than creating new objects when reusable functionality needs to be created, we can often extend an existing type such as a .NET Framework or CLR type. たとえば、拡張メソッドを使用しない場合は、コード内の複数の場所から呼び出すことができる Engine または Query クラスを作成して、SQL サーバーに対するクエリの実行を処理することが考えられます。As an example, if we don't use extension methods, we might create an Engine or Query class to do the work of executing a query on a SQL Server that may be called from multiple places in our code. 一方、代わりに拡張メソッドを使用して System.Data.SqlClient.SqlConnection クラスを拡張すると、SQL サーバーに接続している任意の場所からそのクエリを実行することができます。However we can instead extend the System.Data.SqlClient.SqlConnection class using extension methods to perform that query from anywhere we have a connection to a SQL Server. 他の例としては、System.String クラスへの共通機能の追加、System.IO.File および System.IO.Stream オブジェクトのデータ処理機能の拡張、特定のエラー処理機能のための System.Exception オブジェクトなどがあります。Other examples might be to add common functionality to the System.String class, extend the data processing capabilities of the System.IO.File and System.IO.Stream objects, and System.Exception objects for specific error handling functionality. この種のユース ケースは、開発者の想像力と良識によってのみ制限されます。These types of use-cases are limited only by your imagination and good sense.

struct 型は、メソッドに値で渡されるため、定義済みの型を拡張するのが難かしい場合があります。Extending predefined types can be difficult with struct types because they're passed by value to methods. これは、構造体への変更が構造体のコピーに対して行われることを意味します。That means any changes to the struct are made to a copy of the struct. そのような変更は、拡張メソッドが終了した後では認識できません。Those changes aren't visible once the extension method exits. C# 7.2 以降では、拡張メソッドの最初の引数に ref 修飾子を追加できます。Beginning with C# 7.2, you can add the ref modifier to the first argument of an extension method. ref 修飾子を追加すると、最初の引数が参照によって渡されます。Adding the ref modifier means the first argument is passed by reference. これにより、拡張されている構造体の状態を変更する拡張メソッドを記述できます。This enables you to write extension methods that change the state of the struct being extended.

一般的なガイドラインGeneral Guidelines

オブジェクトのコードを変更したり新しい型を派生させたりすることによって機能を追加することが妥当かつ可能である場合は、そのようにすることがやはり推奨されますが、.NET エコシステムの全体で、拡張メソッドが再利用可能な機能を作成するための重要なオプションになってきています。While it's still considered preferable to add functionality by modifying an object's code or deriving a new type whenever it's reasonable and possible to do so, extension methods have become a crucial option for creating reusable functionality throughout the .NET ecosystem. 元のソースを制御できない場合、派生オブジェクトが不適切または不可能な場合、または該当するスコープを超えて機能を公開してはならない場合は、拡張メソッドが優れた選択肢になります。For those occasions when the original source isn't under your control, when a derived object is inappropriate or impossible, or when the functionality shouldn't be exposed beyond its applicable scope, Extension methods are an excellent choice.

派生型について詳しくは、「継承」をご覧ください。For more information on derived types, see Inheritance.

ソース コードを制御できない型を、拡張メソッドを使用して拡張すると、型の実装の変更により拡張メソッドが破損するというリスクを負うことになります。When using an extension method to extend a type whose source code you aren't in control of, you run the risk that a change in the implementation of the type will cause your extension method to break.

所定の型の拡張メソッドを実装する場合、次の点に注意してください。If you do implement extension methods for a given type, remember the following points:

  • 拡張メソッドが型で定義されているメソッドと同じシグネチャを持つ場合、その拡張メソッドは呼び出されません。An extension method will never be called if it has the same signature as a method defined in the type.
  • 拡張メソッドは名前空間レベルでスコープ内に取り込まれます。Extension methods are brought into scope at the namespace level. たとえば、Extensions という名前の単一の名前空間に、拡張メソッドを含む複数の静的クラスがある場合、using Extensions; ディレクティブによって、それらのすべての拡張メソッドがスコープ内に取り込まれます。For example, if you have multiple static classes that contain extension methods in a single namespace named Extensions, they'll all be brought into scope by the using Extensions; directive.

実装したクラス ライブラリでは、アセンブリのバージョン番号のインクリメントを避けるために、拡張メソッドは使用しないでください。For a class library that you implemented, you shouldn't use extension methods to avoid incrementing the version number of an assembly. ソース コードを所有するライブラリに重要な機能を追加する場合は、アセンブリのバージョン管理について標準の .NET Framework ガイドラインに従う必要があります。If you want to add significant functionality to a library for which you own the source code, you should follow the standard .NET Framework guidelines for assembly versioning. 詳細については、「アセンブリのバージョン管理」を参照してください。For more information, see Assembly Versioning.

関連項目See also