Implementieren von Erweiterungsmethoden für eine Klasse

Abgeschlossen

In der Regel gibt es zwei Möglichkeiten, einem vorhandenen Typ eine Methode hinzuzufügen:

  • Ändern Sie den Quellcode für diesen Typ. Wenn Sie die Quelle ändern, wird eine Änderung erstellt, wenn Sie auch private Datenfelder hinzufügen, um die Methode zu unterstützen.
  • Definieren Sie die neue Methode in einer abgeleiteten Klasse. Eine Methode kann auf diese Weise nicht mithilfe der Vererbung für andere Typen hinzugefügt werden, z. B. Strukturen und Enumerationen. Es kann auch nicht verwendet werden, um einer versiegelten Klasse eine Methode hinzuzufügen.

Mit Erweiterungsmethoden können Sie einem vorhandenen Typ eine Methode "hinzufügen", ohne den Typ selbst zu ändern oder die neue Methode in einem geerbten Typ zu implementieren. Die Erweiterungsmethode muss sich auch nicht in derselben Assembly wie der erweiterte Typ befinden. Sie rufen eine Erweiterungsmethode auf, als wäre sie ein definiertes Element eines Typs.

Erweiterungsmethoden

Mit Erweiterungsmethoden können Sie vorhandene Typen "hinzufügen", ohne einen neuen abgeleiteten Typ zu erstellen, neu zu kompilieren oder anderweitig den ursprünglichen Typ zu ändern. Erweiterungsmethoden sind statische Methoden, werden jedoch aufgerufen, als wären sie Instanzmethoden für den erweiterten Typ. Für Clientcode, der in C#, F# und Visual Basic geschrieben wurde, besteht kein offensichtlicher Unterschied zwischen dem Aufrufen einer Erweiterungsmethode und der in einem Typ definierten Methoden.

Bindungserweiterungsmethoden zur Kompilierungszeit

Sie können Erweiterungsmethoden verwenden, um eine Klasse oder Schnittstelle zu erweitern, aber nicht, um sie außer Kraft zu setzen. Eine Erweiterungsmethode mit demselben Namen und derselben Signatur wie eine Klassenmethode wird nie aufgerufen. Zur Kompilierungszeit haben Erweiterungsmethoden immer eine niedrigere Priorität als Instanzenmethoden, die im Typ selbst definiert sind. Anders ausgedrückt: Wenn ein Typ eine Methode mit dem Namen Process(int i)hat und Sie über eine Erweiterungsmethode mit derselben Signatur verfügen, bindet der Compiler immer an die Instanzmethode. Wenn der Compiler auf einen Methodenaufruf stößt, wird in den Instanzmethoden des Typs nach einer Übereinstimmung gesucht. Wenn keine übereinstimmende Instanzmethode gefunden wird, sucht der Compiler nach erweiterungsmethoden, die für den Typ definiert sind. Der Compiler bindet an die erste gefundene Erweiterungsmethode.

Das folgende Beispiel veranschaulicht die Regeln, die der C#-Compiler folgt, um zu bestimmen, ob ein Methodenaufruf an eine Instanzmethode für den Typ oder an eine Erweiterungsmethode gebunden werden soll.


namespace ExtensionMethodDemo.PersonNamespace
{
    // Define a simple class with properties and methods
    public class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int Age { get; set; }

        public void Introduce()
        {
            Console.WriteLine($"Hi, I'm {FirstName} {LastName}, and I'm {Age} years old.");
        }
    }
}

namespace ExtensionMethodDemo.PersonExtensionsNamespace
{
    using ExtensionMethodDemo.PersonNamespace;

    // Define an extension method for the Person class
    public static class PersonExtensions
    {
        public static void DisplayFullName(this Person person)
        {
            Console.WriteLine($"Full Name: {person.FirstName} {person.LastName}");
        }

        public static bool IsAdult(this Person person)
        {
            return person.Age >= 18;
        }

        public static void Introduce()
        {
            Console.WriteLine($"Extension - Hi, it's nice to meet you.");
        }

        // Extension method attempting to override Introduce
        public static void Introduce(this Person person, string greeting)
        {
            Console.WriteLine($"{greeting}, I'm {person.FirstName} {person.LastName}, and I'm {person.Age} years old.");
        }
    }
}

namespace ExtensionMethodDemo
{
    using ExtensionMethodDemo.PersonNamespace;
    using ExtensionMethodDemo.PersonExtensionsNamespace;

    class Program
    {
        static void Main(string[] args)
        {
            // Create instances of the Person class
            Person person1 = new Person { FirstName = "FName1", LastName = "LName1", Age = 25 };
            Person person2 = new Person { FirstName = "FName2", LastName = "LName2", Age = 16 };

            // Use the methods of the Person class
            person1.Introduce();
            person2.Introduce();

            // Use the extension methods
            person1.DisplayFullName();
            Console.WriteLine($"Is {person1.FirstName} an adult? {person1.IsAdult()}");

            person2.DisplayFullName();
            Console.WriteLine($"Is {person2.FirstName} an adult? {person2.IsAdult()}");

            // Use the extension method that attempts to override Introduce
            person1.Introduce("Hello");
            person2.Introduce("Greetings");
        }
    }
}
// Output:
//     Hi, I'm FName1 LName1, and I'm 25 years old.
//     Hi, I'm FName2 LName2, and I'm 16 years old.
//     Full Name: FName1 LName1
//     Is FName1 an adult? True
//     Full Name: FName2 LName2
//     Is FName2 an adult? False
//     Hello, I'm FName1 LName1, and I'm 25 years old.
//     Greetings, I'm FName2 LName2, and I'm 16 years old.

In diesem Beispiel verfügt die Person-Klasse über drei Eigenschaften: FirstName, LastNameund Age. Die Person Klasse verfügt auch über eine Instanzmethode Introduce, die eine Nachricht in die Konsole schreibt.

Die PersonExtensions Klasse definiert Erweiterungsmethoden für die Person Klasse. Die PersonExtensions Klasse enthält die folgenden Erweiterungsmethoden:

  • DisplayFullName: Schreibt den vollständigen Namen der Person in die Konsole.
  • IsAdult: Gibt einen booleschen Wert zurück, der angibt, ob die Person ein Erwachsener ist.
  • Introduce: Schreibt eine Nachricht in die Konsole. Diese Methode hat zwei Überladungen: eine ohne Parameter und eine mit einem Zeichenfolgenparameter.
  • Introduce(this Person person, string greeting): Schreibt eine Nachricht mit einer benutzerdefinierten Begrüßung in die Konsole.
  • Introduce(): Schreibt eine Standardmeldung in die Konsole.

Die Main-Methode erstellt zwei Instanzen der Person Klasse und ruft die Introduce-Methode für jede Instanz auf. Die in der Introduce Klasse definierte Person-Methode wird ausgeführt. Die Introduce-Methode ohne Parameter in der PersonExtensions-Klasse wird nicht aufgerufen, da die Person Klasse bereits eine Instanzmethode Introduce mit derselben Signatur aufweist. Der Compiler folgt den Regeln für bindungserweiterungsmethoden zur Kompilierungszeit und gibt der Instanzmethode Vorrang, die im Typ selbst definiert ist. Die Main-Methode ruft auch die Erweiterungsmethoden DisplayFullName, IsAdultund die überladene Introduce-Methode mit einer benutzerdefinierten Begrüßung auf. Die Erweiterungsmethoden werden wie erwartet ausgeführt.

Allgemeine Richtlinien

Erweiterungsmethoden sind eine wichtige Option zum Erstellen wiederverwendbarer Funktionen im gesamten .NET-Ökosystem. Das Ändern des Codes eines Objekts oder das Ableiten eines neuen Typs, wenn dies sinnvoll und möglich ist, wird jedoch weiterhin als bevorzugt betrachtet. Erweiterungsmethoden sind eine hervorragende Wahl, wenn die ursprüngliche Quelle nicht unter Ihrer Kontrolle ist, wenn ein abgeleitetes Objekt unangemessen oder unmöglich ist oder wenn die Funktionalität nicht über ihren anwendbaren Bereich hinaus verfügbar gemacht werden sollte.

Wenn Sie Erweiterungsmethoden verwenden, um einen Typ zu erweitern, dessen Quellcode Sie nicht kontrollieren, führen Sie das Risiko aus, dass eine Änderung der Implementierung des Typs dazu führt, dass die Erweiterungsmethode abgebrochen wird. Wenn Sie Erweiterungsmethoden für einen bestimmten Typ implementieren, denken Sie an die folgenden Punkte:

  • Eine Erweiterungsmethode wird nicht aufgerufen, wenn sie dieselbe Signatur wie eine im Typ definierte Methode aufweist.
  • Erweiterungsmethoden werden auf Namespaceebene in den Bereich gebracht. Wenn Sie z. B. über mehrere statische Klassen verfügen, die Erweiterungsmethoden in einem einzigen Namespace mit dem Namen Extensionsenthalten, werden sie alle durch die using Extensions;-Direktive in den Gültigkeitsbereich gebracht.

Für eine von Ihnen implementierte Klassenbibliothek sollten Sie keine Erweiterungsmethoden verwenden, um zu vermeiden, dass die Versionsnummer einer Assembly erhöht wird. Wenn Sie einer Bibliothek, für die Sie den Quellcode besitzen, erhebliche Funktionen hinzufügen möchten, befolgen Sie die .NET-Richtlinien für die Assemblyversionsverwaltung.