Implementer udvidelsesmetoder for en klasse

Fuldført

Normalt er der to måder at føje en metode til en eksisterende type på:

  • Rediger kildekoden for den pågældende type. Hvis du ændrer kilden, oprettes der en banebrydende ændring, hvis du også tilføjer private datafelter for at understøtte metoden.
  • Definer den nye metode i en afledt klasse. En metode kan ikke tilføjes på denne måde ved hjælp af nedarvning for andre typer, f.eks. strukturer og optællinger. Den kan heller ikke bruges til at "føje" en metode til en forseglet klasse.

Udvidelsesmetoder giver dig mulighed for at "føje" en metode til en eksisterende type uden at ændre selve typen eller implementere den nye metode i en nedarvet type. Udvidelsesmetoden behøver heller ikke at være placeret i den samme assembly som den type, den udvider. Du kalder en udvidelsesmetode, som om den var et defineret medlem af en type.

Udvidelsesmetoder

Udvidelsesmetoder giver dig mulighed for at "føje" metoder til eksisterende typer uden at oprette en ny afledt type, kompilere igen eller på anden måde ændre den oprindelige type. Udvidelsesmetoder er statiske metoder, men de kaldes, som om de var forekomstmetoder for den udvidede type. For klientkode, der er skrevet i C#, F# og Visual Basic, er der ingen synlig forskel mellem at kalde en udvidelsesmetode og de metoder, der er defineret i en type.

Metoder til bindingsudvidelse på kompileringstidspunktet

Du kan bruge udvidelsesmetoder til at udvide en klasse eller grænseflade, men ikke til at tilsidesætte dem. En udvidelsesmetode med samme navn og signatur som en klassemetode kaldes aldrig. På kompileringstidspunktet har udvidelsesmetoder altid lavere prioritet end instansmetoder, der er defineret i selve typen. Hvis en type med andre ord har en metode med navnet Process(int i), og du har en udvidelsesmetode med den samme signatur, bindes compileren altid til forekomstmetoden. Når compileren støder på en metodeaktivering, søger den efter et match i typens forekomstmetoder. Hvis der ikke findes en tilsvarende forekomstmetode, søger compileren efter alle udvidelsesmetoder, der er defineret for typen. Compileren binder til den første udvidelsesmetode, der findes.

I følgende eksempel vises de regler, som C#-compileren følger for at afgøre, om et metodekald skal bindes til en forekomstmetode for typen eller til en udvidelsesmetode.


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.

I dette eksempel har klassen Person tre egenskaber: FirstName, LastNameog Age. Klassen Person har også en forekomstmetode Introduce, der skriver en meddelelse til konsollen.

Klassen PersonExtensions definerer udvidelsesmetoder for klassen Person. Klassen PersonExtensions indeholder følgende udvidelsesmetoder:

  • DisplayFullName: Skriver personens fulde navn til konsollen.
  • IsAdult: Returnerer en boolesk værdi, der angiver, om personen er voksen.
  • Introduce: Skriver en meddelelse til konsollen. Denne metode har to overbelastninger: én uden parametre og én med en strengparameter.
  • Introduce(this Person person, string greeting): Skriver en meddelelse til konsollen med en brugerdefineret hilsen.
  • Introduce(): Skriver en standardmeddelelse til konsollen.

Metoden Main opretter to forekomster af klassen Person og kalder metoden Introduce for hver forekomst. Den Introduce metode, der er defineret i klassen Person, udføres. Metoden Introduce uden parametre i klassen PersonExtensions kaldes ikke, fordi den Person klasse allerede har en forekomstmetode Introduce med den samme signatur. Compileren følger reglerne for bindingsudvidelsesmetoder på kompileringstidspunktet og prioriterer den forekomstmetode, der er defineret i selve typen. Metoden Main kalder også udvidelsesmetoderne DisplayFullName, IsAdultog den overbelastede Introduce-metode med en brugerdefineret hilsen. Udvidelsesmetoderne udføres som forventet.

Generelle retningslinjer

Udvidelsesmetoder er en vigtig mulighed for at oprette genbrugsfunktionalitet i hele .NET-økosystemet. Ændring af et objekts kode eller afledt en ny type, når det er rimeligt og muligt at gøre det, anses dog stadig for at være at foretrække. Udvidelsesmetoder er et glimrende valg, når den oprindelige kilde ikke er under din kontrol, når et afledt objekt er upassende eller umuligt, eller når funktionaliteten ikke skal vises uden for dets relevante omfang.

Når du bruger udvidelsesmetoder til at udvide en type, hvis kildekode du ikke har kontrol over, risikerer du, at en ændring i implementeringen af typen medfører, at udvidelsesmetoden afbrydes. Hvis du implementerer udvidelsesmetoder for en bestemt type, skal du huske følgende punkter:

  • En udvidelsesmetode kaldes ikke, hvis den har den samme signatur som en metode, der er defineret i typen .
  • Udvidelsesmetoder overføres til området på navneområdeniveau. Hvis du f.eks. har flere statiske klasser, der indeholder udvidelsesmetoder i et enkelt navneområde med navnet Extensions, bliver de alle omfattet af using Extensions;-direktivet.

I forbindelse med et klassebibliotek, som du implementerede, skal du ikke bruge udvidelsesmetoder for at undgå at øge versionsnummeret for en assembly. Hvis du vil føje betydelig funktionalitet til et bibliotek, hvor du ejer kildekoden, skal du følge .NET-retningslinjerne for assemblyversionsstyring.