Erweiterungsmethoden (C#-Programmierhandbuch)

Aktualisiert: November 2007

Mit Erweiterungsmethoden können Sie vorhandenen Typen Methoden hinzufügen, ohne einen neuen abgeleiteten Typ zu erstellen und ohne den ursprünglichen Typ neu kompilieren oder auf andere Weise bearbeiten zu müssen. Erweiterungsmethoden sind eine besondere Art von statischen Methoden, die Sie jedoch wie Instanzmethoden für den erweiterten Typ aufrufen können. Für in C# und Visual Basic geschriebenen Clientcode gibt es keinen sichtbaren Unterschied zwischen dem Aufrufen einer Erweiterungsmethode und den Methoden, die in einem Typ tatsächlich definiert sind.

Die gebräuchlichsten Erweiterungsmethoden sind die LINQ-Standardabfrageoperatoren, mit denen Sie dem vorhandenen System.Collections.IEnumerable-Typ und dem vorhandenen System.Collections.Generic.IEnumerable<T>-Typ Abfragefunktionen hinzufügen können. Um die Standardabfrageoperatoren zu verwenden, müssen Sie sie zuerst mit einer using System.Linq-Direktive einbinden. Jeder Typ, der IEnumerable<T> implementiert, scheint Instanzmethoden zu haben, wie z. B. GroupBy, OrderBy, Average. Sie können diese zusätzlichen Methoden in der IntelliSense-Anweisungsvervollständigung sehen, wenn Sie nach einer Instanz eines IEnumerable<T>-Typs, z. B. List<T> oder Array, "dot" eingeben.

Das folgende Beispiel zeigt, wie Sie die Standardabfrageoperator-Methode OrderBy für ein Ganzzahlarray aufrufen können. Der Ausdruck in Klammern ist ein Lambda-Ausdruck. Viele Standardabfrageoperatoren verwenden Lambda-Ausdrücke als Parameter, dies ist jedoch keine Voraussetzung für Erweiterungsmethoden. Weitere Informationen finden Sie unter Lambda-Ausdrücke (C#-Programmierhandbuch).

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

Erweiterungsmethoden werden als statische Methoden definiert, jedoch mithilfe einer Instanzmethodensyntax aufgerufen. Der erste Parameter bestimmt, für welchen Typ die Methode gilt, und vor dem Parameter steht der this-Modifizierer. Erweiterungsmethoden befinden sich nur dann im Bereich, wenn Sie den Namespace explizit mit einer using-Direktive in Ihren Quellcode importieren.

Im folgenden Beispiel wird eine für die System.String-Klasse definierte Erweiterungsmethode veranschaulicht. Beachten Sie, dass sie in einer nicht geschachtelten, nicht generischen statischen Klasse definiert wird:

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

Die WordCount-Erweiterungsmethode kann mit dieser using-Direktive eingebunden werden:

using ExtensionMethods;

Sie kann darüber hinaus mit dieser Syntax von einer Anwendung aufgerufen werden:

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

Im Code rufen Sie die Erweiterungsmethode mit Instanzmethodensyntax auf. Die vom Compiler erstellte Intermediate Language (IL) übersetzt jedoch Ihren Code in einen Aufruf der statischen Methode. Daher wird nicht wirklich gegen das Prinzip der Kapselung verstoßen. Erweiterungsmethoden können vielmehr nicht auf private Variablen im Typ zugreifen, den sie erweitern.

Weitere Informationen finden Sie unter Gewusst wie: Implementieren und Aufrufen einer benutzerdefinierten Erweiterungsmethode (C#-Programmierhandbuch).

Im Allgemeinen werden Sie vermutlich viel häufiger Erweiterungsmethoden aufrufen, anstatt Ihre eigenen zu implementieren. Da Erweiterungsmethoden mit der Instanzmethodensyntax aufgerufen werden, sind für ihren Einsatz aus dem Clientcode keine besonderen Kenntnisse erforderlich. Um Erweiterungsmethoden für einen bestimmten Typ zu aktivieren, fügen Sie eine using-Direktive für den Namespace, in dem die Methoden definiert werden, hinzu. Um beispielsweise die Standardabfrageoperatoren zu verwenden, fügen Sie diese using-Direktive dem Code hinzu:

using System.Linq;

(Möglicherweise müssen Sie auch einen Verweis auf System.Core.dll hinzufügen.) Wie Sie sehen, werden die Standardabfrageoperatoren jetzt in IntelliSense als zusätzliche Methoden, die für die meisten IEnumerable<T>-Typen verfügbar sind, angezeigt.

Hinweis:

Obwohl Standardabfrageoperatoren nicht in IntelliSense für String angezeigt werden, sind sie dennoch verfügbar.

Binden von Erweiterungsmethoden während der Kompilierung

Sie können Erweiterungsmethoden verwenden, um eine Klasse oder eine Schnittstelle zu erweitern, jedoch nicht, um sie zu überschreiben. Eine Erweiterungsmethode mit dem gleichen Namen und der gleichen Signatur wie eine Schnittstellen- oder Klassenmethode wird nie aufgerufen. Bei der Kompilierung verfügen Erweiterungsmethoden immer über niedrigere Priorität als im Typ selbst definierte Instanzmethoden. Das heißt, wenn ein Typ eine Methode mit dem Namen Process(int i) hat und Sie über eine Erweiterungsmethode mit der gleichen Signatur verfügen, stellt der Compiler immer eine Bindung mit der Instanzmethode her. Wenn der Compiler einen Methodenaufruf erkennt, sucht er zuerst nach einer Entsprechung in den Instanzmethoden des Typs. Wenn keine Entsprechung gefunden wird, sucht er nach Erweiterungsmethoden, die für den Typ definiert wurden, und stellt eine Bindung mit der ersten gefundenen Erweiterungsmethode her. Im folgenden Beispiel wird veranschaulicht, wie der Compiler bestimmt, mit welcher Erweiterungsmethode oder Instanzmethode die Bindung erfolgen soll.

Beispiel

Das folgende Beispiel veranschaulicht die Regeln, denen der C#-Compiler folgt, um zu bestimmen, ob ein Methodenaufruf an eine Instanzmethode für den Typ oder eine Erweiterungsmethode gebunden werden soll. Die statische Klasse Extensions enthält Erweiterungsmethoden, die für jeden Typ definiert wurden, der IMyInterface implementiert. Die Klassen A, B und C implementieren alle die Schnittstelle.

Die MethodB-Methode wird nie aufgerufen, da der Name und die Signatur genau mit Methoden übereinstimmen, die bereits von den Klassen implementiert wurden.

Wenn der Compiler keine Instanzmethode mit einer entsprechenden Signatur findet, stellt er ggf. eine Bindung mit einer entsprechenden Erweiterungsmethode her.

namespace Extensions
{
  using System;
  using ExtensionMethodsDemo1;

     // Define extension methods for any type 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, because the three classes implement MethodB.
        public static void MethodB(this IMyInterface myInterface) 
        {
            Console.WriteLine("Extension.MethodB(this IMyInterface myInterface)");
        }
    }
}
namespace ExtensionMethodsDemo1
{
    using System;
    using Extensions;

    public interface IMyInterface
    {
        void MethodB();
    }

    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)
        {
            A a = new A();
            B b = new B();
            C c = new C();
            TestMethodBinding(a,b,c);
        }

        static void TestMethodBinding(A a, B b, C c)
        {
            // A has no methods, so each call resolves to 
            // the extension methods whose signatures match.
            a.MethodA(1);           // Extension.MethodA(object, int)
            a.MethodA("hello");     // Extension.MethodA(object, string)
            a.MethodB();            // A.MethodB()

            // B itself has a method with this signature.
            b.MethodA(1);           // B.MethodA(int)
            b.MethodB();            // B.MethodB()

            // B has no matching method, but Extension does.
            b.MethodA("hello");     // Extension.MethodA(object, string)

            // In each case C has a matching instance method.
            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()
 */

Allgemeine Richtlinien

Im Allgemeinen wird empfohlen, dass Sie Erweiterungsmethoden sparsam und nur wenn unbedingt notwendig implementieren. Wenn möglich sollte der Clientcode, der einen vorhandenen Typ erweitern muss, dies durch die Erstellung eines neuen vom vorhandenen Typ abgeleiteten Typs durchführen. Weitere Informationen finden Sie unter Vererbung (C#-Programmierhandbuch).

Wenn Sie eine Erweiterungsmethode zum Erweitern eines Typs, dessen Quellcode Sie nicht ändern können, verwenden, laufen Sie Gefahr, dass eine Änderung an der Implementierung des Typs eine Unterbrechung der Erweiterungsmethode bewirkt.

Wenn Sie Erweiterungsmethoden für einen gegebenen Typ implementieren, beachten Sie die folgenden zwei Punkte:

  • Eine Erweiterungsmethode wird nie aufgerufen, wenn sie die gleiche Signatur wie eine im Typ definierte Methode hat.

  • Erweiterungsmethoden werden auf Namespace-Ebene eingebunden. Wenn Sie z. B. mehrere statische Klassen haben, die Erweiterungsmethoden in einem einzelnen Namespace mit dem Namen Extensions enthalten, werden sie alle mit der using Extensions;-Direktive eingebunden.

Implementierer von Klassenbibliotheken sollten keine Erweiterungsmethoden verwenden, um zu vermeiden, dass neue Versionen von Assemblys erstellt werden. Wenn Sie einer Bibliothek wichtige, neue Funktionen hinzufügen möchten und Sie der Besitzer des Quellcodes sind, sollten Sie die .NET Framework-Richtlinien für Assemblyversioning befolgen. Weitere Informationen finden Sie unter Assemblyversionen.

Siehe auch

Konzepte

C#-Programmierhandbuch

Übersicht über Standardabfrageoperatoren

Referenz

Lambda-Ausdrücke (C#-Programmierhandbuch)

Weitere Ressourcen

Conversion rules for Instance parameters and their impact

Extension methods Interoperability between languages

Extension methods and Curried Delegates