C# Academy · Lektion

Opløsning af extension methods

Forstå, hvordan compileren finder extension methods.

Lektion 3 af 413 trin

Opløsning af extension methods er en gratis C# Academy-lektion på CoddyKit. Dette er lektion 3 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i C# Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. C# Academy-kurset indeholder 4 lektioner i alt.

Sådan finder compileren udvidelser

Når du skriver value.Method(), og ingen instansmetode passer, leder compileren efter en udvidelsesmetode. Den overvejer dog kun udvidelser, hvis indeholdende navneområde er i synlighedsområdet via et using-direktiv.

using-direktiver gør udvidelser synlige

En udvidelse er kun synlig, hvis du importerer dens navneområde. Uden det rigtige using ser metoden ud til ikke at findes, selv om den er kompileret ind i programsamlingen.

using System;
using MyHelpers; // brings StringExtensions into scope

namespace MyHelpers
{
    public static class StringExtensions
    {
        public static string Reverse(this string s)
        {
            var arr = s.ToCharArray();
            Array.Reverse(arr);
            return new string(arr);
        }
    }
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine("hello".Reverse());
    }
}

Instansmetoder undersøges først

Compileren løser kaldet i trin: Først søger den efter en matchende instansmetode, herunder nedarvede metoder. Kun hvis ingen passer, søger den efter udvidelsesmetoder.

using System;
using System.Collections.Generic;

public static class ListExt
{
    // Ignored: List<int> already has Add(int)
    public static void Add(this List<int> list, int a, int b)
        => list.Add(a + b);
}

public class Program
{
    public static void Main()
    {
        var list = new List<int>();
        list.Add(5); // the built-in instance Add wins for this signature
        Console.WriteLine(list[0]);
    }
}

Nærmere navneområder foretrækkes

Hvis to udvidelser passer, vinder den, hvis navneområde er nærmest i indlejringshierarkiet. En tvetydighed, som compileren ikke kan løse, medfører en kompileringsfejl, så du tvinges til at fjerne tvetydigheden.

using System;

namespace Outer
{
    public static class A { public static string Tag(this int n) => "A" + n; }

    namespace Inner
    {
        public static class B { public static string Tag(this int n) => "B" + n; }

        public class Program
        {
            public static void Main()
            {
                // B is in the nearer (Inner) namespace, so it is preferred
                Console.WriteLine(7.Tag());
            }
        }
    }
}

Fjern tvetydighed med et statisk kald

Når udvidelser er i konflikt, eller du vil være eksplicit, kan du kalde den statiske metode direkte ved hjælp af klassens fulde navn. Det går helt uden om opløsningen af udvidelser.

using System;

public static class FormatExt
{
    public static string Bracket(this string s) => "[" + s + "]";
}

public class Program
{
    public static void Main()
    {
        // Explicit static call, no ambiguity possible
        Console.WriteLine(FormatExt.Bracket("x"));
    }
}

Valg mellem overbelastede udvidelser

Hvis flere udvidelsesmetoder har samme navn, vælger den almindelige opløsning af overbelastninger det bedste match ud fra parametertyperne, ligesom ved almindelig overbelastning af metoder.

using System;

public static class PrintExt
{
    public static string Describe(this int n) => "int:" + n;
    public static string Describe(this string s) => "str:" + s;
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine(42.Describe());
        Console.WriteLine("hi".Describe());
    }
}

Typeudledning under opløsning

For generiske udvidelser udleder compileren typeargumenterne ud fra modtageren og argumenterne. Hvis typeudledningen mislykkes, tages udvidelsen ikke i betragtning.

using System;
using System.Collections.Generic;

public static class SeqExt
{
    public static T FirstItem<T>(this IEnumerable<T> source)
    {
        foreach (var item in source) return item;
        throw new InvalidOperationException("empty");
    }
}

public class Program
{
    public static void Main()
    {
        var nums = new List<int> { 5, 6, 7 };
        // T inferred as int from the List<int> receiver
        Console.WriteLine(nums.FirstItem());
    }
}

Globale using-direktiver og implicitte importer

Moderne C# kan erklære global using-direktiver, så et navneområde er synligt i hele projektet. Det gør fælles udvidelsesbiblioteker tilgængelige overalt uden gentagne importer.

using System;

// In a real project a separate file might say: global using MyHelpers;
namespace MyHelpers
{
    public static class IntExt { public static int Twice(this int n) => n * 2; }
}

public class Program
{
    public static void Main()
    {
        // Works here because MyHelpers is referenced explicitly
        Console.WriteLine(MyHelpers.IntExt.Twice(8));
    }
}

Når modtagertypen er afgørende

Opløsningen afhænger af modtagerens statiske type, ikke af typen ved kørsel. En udvidelse, der er erklæret for en basistype, gælder også, når variablen indeholder en afledt instans, medmindre en mere specifik udvidelse er synlig.

using System;

public class Animal { }
public class Cat : Animal { }

public static class AnimalExt
{
    public static string Kind(this Animal a) => "animal";
}

public class Program
{
    public static void Main()
    {
        Animal a = new Cat();
        // Chosen by static type Animal
        Console.WriteLine(a.Kind());
    }
}

En model for opløsning

Tænk på rækkefølgen sådan: (1) instansmetoder, derefter (2) udvidelsesmetoder fra importerede navneområder, med de nærmeste først, og derefter (3) opløsning af overbelastninger blandt kandidaterne. Når du kender denne rækkefølge, forklarer den næsten alle overraskende resultater.

using System;

public static class Ext
{
    public static string Wrap(this string s) => "<" + s + ">";
}

public class Program
{
    public static void Main()
    {
        // No instance Wrap exists, so the extension is used
        Console.WriteLine("node".Wrap());
    }
}

Prøv selv

Definér en udvidelse i et navneområde, importér det, og kald udvidelsen. Kald derefter den samme metode eksplicit som en statisk metode for at se, at begge veje fungerer.

using System;
using Text.Helpers;

namespace Text.Helpers
{
    public static class StringExtensions
    {
        public static int Vowels(this string s)
        {
            int count = 0;
            foreach (var c in s.ToLower())
                if ("aeiou".IndexOf(c) >= 0) count++;
            return count;
        }
    }
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine("education".Vowels());
        Console.WriteLine(Text.Helpers.StringExtensions.Vowels("education"));
    }
}

Hurtigt tjek

Overvej, hvad der gør en udvidelsesmetode synlig.

Opsummering

Opløsning af udvidelsesmetoder følger en klar rækkefølge.

  • Instansmetoder prøves først; udvidelser udfylder kun hullerne.
  • En udvidelse er kun synlig, hvis dens navneområde er i synlighedsområdet via using eller global using.
  • Nærmere navneområder vinder; uafklarede uafgjortheder medfører kompileringsfejl.
  • Du kan altid kalde den statiske metode eksplicit for at fjerne tvetydighed.
  • Opløsningen bruger modtagerens statiske type.
Gratis at komme i gang

Lær C# med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
93
Lektioner
346

Ofte stillede spørgsmål

Er lektionen “Opløsning af extension methods” gratis?

Ja — hele teksten til “Opløsning af extension methods” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af C# Academy-kurset, skal du opgradere til CoddyKit PRO. C# Academy-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “Opløsning af extension methods”?

Forstå, hvordan compileren finder extension methods. Du øver dig i C# Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på C# Academy?

Der kræves ingen tidligere erfaring. C# Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 3 af 4.

Hvor lang tid tager lektionen “Opløsning af extension methods”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne C# Academy-lektion?

Ja. Alle C# Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. Definition af extension methods
  2. Udvidelse af interfaces og generics
  3. Opløsning af extension methods
  4. Design af gode extensions
← Tilbage til C# Academy