Implementere utvidelsesmetoder for en klasse

Fullført

Vanligvis finnes det to måter å legge til en metode på i en eksisterende type:

  • Endre kildekoden for denne typen. Hvis du endrer kilden, opprettes det en bruddendring hvis du også legger til private datafelt for å støtte metoden.
  • Definer den nye metoden i en avledet klasse. En metode kan ikke legges til på denne måten ved hjelp av arv for andre typer, for eksempel strukturer og opplistinger. Den kan heller ikke brukes til å «legge til» en metode i en forseglet klasse.

Med utvidelsesmetoder kan du «legge til» en metode i en eksisterende type uten å endre selve typen eller implementere den nye metoden i en arvet type. Utvidelsesmetoden trenger heller ikke å ligge i samme samling som typen den utvider. Du kaller en utvidelsesmetode som om den var et definert medlem av en type.

Utvidelsesmetoder

Med utvidelsesmetoder kan du legge til metoder for eksisterende typer uten å opprette en ny avledet type, kompilere på nytt eller på annen måte endre den opprinnelige typen. Utvidelsesmetoder er statiske metoder, men de kalles som om de var forekomstmetoder på den utvidede typen. For klientkode skrevet i C#, F# og Visual Basic, er det ingen åpenbar forskjell mellom å kalle opp en utvidelsesmetode og metodene som er definert i en type.

Bindingsutvidelsesmetoder på kompileringstidspunktet

Du kan bruke utvidelsesmetoder til å utvide en klasse eller et grensesnitt, men ikke for å overstyre dem. En utvidelsesmetode med samme navn og signatur som en klassemetode kalles aldri. På kompileringstidspunktet har utvidelsesmetoder alltid lavere prioritet enn forekomstmetoder som er definert i selve typen. Hvis en type med andre ord har en metode som heter Process(int i), og du har en utvidelsesmetode med samme signatur, binder kompilatoren seg alltid til forekomstmetoden. Når kompilatoren støter på en metodeaktivering, ser den etter et treff i typens forekomstmetoder. Hvis det ikke blir funnet noen samsvarende forekomstmetode, søker kompilatoren etter eventuelle utvidelsesmetoder som er definert for typen. Kompilatoren binder seg til den første utvidelsesmetoden den finner.

Følgende eksempel viser reglene som C#-kompilatoren følger for å avgjøre om du vil binde et metodekall til en forekomstmetode på typen, eller til en utvidelsesmetode.


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 eksemplet har Person klassen tre egenskaper: FirstName, LastNameog Age. Klassen Person har også en forekomstmetode Introduce som skriver en melding til konsollen.

Klassen PersonExtensions definerer utvidelsesmetoder for Person klassen. Klassen PersonExtensions inneholder følgende utvidelsesmetoder:

  • DisplayFullName: Skriver hele navnet på personen til konsollen.
  • IsAdult: Returnerer en boolsk verdi som angir om personen er voksen.
  • Introduce: Skriver en melding til konsollen. Denne metoden har to overbelastninger: én uten parametere og én med en strengparameter.
  • Introduce(this Person person, string greeting): Skriver en melding til konsollen med en egendefinert hilsen.
  • Introduce(): Skriver en standardmelding til konsollen.

Metoden Main oppretter to forekomster av Person-klassen og kaller Introduce metoden for hver forekomst. Den Introduce metoden som er definert i Person klassen, utføres. Den Introduce metoden uten parametere i PersonExtensions klassen kalles ikke fordi Person klassen allerede har en forekomstmetode Introduce med samme signatur. Kompilatoren følger reglene for bindingsutvidelsesmetoder på kompileringstidspunktet og prioriterer forekomstmetoden som er definert i selve typen. Metoden Main kaller også utvidelsesmetodene DisplayFullName, IsAdultog den overbelastede Introduce metoden med en egendefinert hilsen. Utvidelsesmetodene kjøres som forventet.

Generelle retningslinjer

Utvidelsesmetoder er et viktig alternativ for å opprette gjenbrukbar funksjonalitet i hele .NET-økosystemet. Men å endre koden for et objekt eller utlede en ny type når det er rimelig og mulig å gjøre dette, anses likevel som å foretrekke. Utvidelsesmetoder er et utmerket valg når den opprinnelige kilden ikke er under din kontroll, når et avledet objekt er upassende eller umulig, eller når funksjonaliteten ikke skal eksponeres utover gjeldende omfang.

Når du bruker utvidelsesmetoder til å utvide en type med kildekoden du ikke har kontroll over, risikerer du at en endring i implementeringen av typen fører til at utvidelsesmetoden brytes. Hvis du implementerer utvidelsesmetoder for en gitt type, må du huske følgende punkter:

  • En utvidelsesmetode kalles ikke hvis den har samme signatur som en metode som er definert i typen.
  • Utvidelsesmetoder hentes inn i omfanget på navneområdenivå. Hvis du for eksempel har flere statiske klasser som inneholder utvidelsesmetoder i ett enkelt navneområde med navnet Extensions, hentes alle inn i omfanget av using Extensions;-direktivet.

For et klassebibliotek du implementerte, bør du ikke bruke utvidelsesmetoder for å unngå å øke versjonsnummeret for en samling. Hvis du vil legge til betydelig funksjonalitet i et bibliotek der du eier kildekoden, følger du .NET-retningslinjene for samlingsversjon.