拡張メソッド (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 a special kind of static method, but they are 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 is no apparent difference between calling an extension method and the methods that are actually defined in a type.

最も一般的な拡張メソッドは、既存の LINQLINQ 型および System.Collections.IEnumerable 型にクエリ機能を追加する System.Collections.Generic.IEnumerable<T> 標準クエリ演算子です。The most common extension methods are the LINQLINQ 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 メソッドを呼び出す方法を次の例に示します。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 is not 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. 最初のパラメーターでは、メソッドが操作する型を指定します。このパラメーターの前には this 修飾子を付加します。Their first parameter specifies which type the method operates on, and 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. 入れ子になっていない、非ジェネリックの静的クラス内で定義されていることに注意してください。Note that it is 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();  

コードでは、インスタンス メソッドの構文を使用して拡張メソッドを呼び出します。In your code you invoke the extension method with instance method syntax. ただし、コンパイラが生成する中間言語 (IL: Intermediate Language) により、コードは静的メソッドに対する呼び出しに変換されます。However, the intermediate language (IL) generated by the compiler translates your code into a call on the static method. したがって、カプセル化の原則には実質的に違反していません。Therefore, the principle of encapsulation is not really being violated. 実際に、拡張メソッドは、それらが拡張している型のプライベート変数にはアクセスできません。In fact, 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 will 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 will notice that the standard query operators now appear in IntelliSense as additional methods available for most IEnumerable<T> types.

注意

String の場合、IntelliSense により標準クエリ演算子は表示されませんが、利用できます。Although standard query operators do not appear in IntelliSense for String, they are still available.

コンパイル時の拡張メソッドのバインディング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 cannot 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(object, int)
            a.MethodA("hello");     // Extension.MethodA(object, 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(object, 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()
 */

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

拡張メソッドは、一般的に、必要な場合に限り注意して実装することをお勧めします。In general, we recommend that you implement extension methods sparingly and only when you have to. クライアント コードで既存の型を拡張する必要がある場合、可能であれば既存の型から派生した新しい型を作成することで行ってください。Whenever possible, client code that must extend an existing type should do so by creating a new type derived from the existing type. 詳細については、「継承」を参照してください。For more information, see Inheritance.

拡張メソッドを使用して、変更できないソース コードのある型を拡張する場合、型の実装の変更により拡張メソッドが破損するというリスクを負うことになります。When using an extension method to extend a type whose source code you cannot change, 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 will 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

C# プログラミング ガイドC# Programming Guide
並列プログラミングのサンプル (拡張メソッドの例が多数掲載されています)Parallel Programming Samples (these include many example extension methods)
ラムダ式Lambda Expressions
標準クエリ演算子の概要Standard Query Operators Overview
インスタンス パラメーターの変換規則とその影響Conversion rules for Instance parameters and their impact
拡張メソッドの言語間での相互運用性Extension methods Interoperability between languages
拡張メソッドとカリー化デリゲートExtension methods and Curried Delegates
バインディングとエラー報告に関する拡張メソッドExtension method Binding and Error reporting