Rozstrzyganie metod rozszerzających
Poznaj sposób wyszukiwania rozszerzeń przez kompilator.
Rozstrzyganie metod rozszerzających to bezpłatna lekcja C# Academy na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej C# Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs C# Academy zawiera 4 lekcji w sumie.
Jak kompilator znajduje metody rozszerzające
Gdy zapis value.Method() nie pasuje do żadnej metody instancji, kompilator szuka metody rozszerzającej. Uwzględnia jednak tylko te rozszerzenia, których przestrzeń nazw jest dostępna dzięki dyrektywie using.
Dyrektywy using udostępniają metody rozszerzające
Metoda rozszerzająca jest widoczna tylko wtedy, gdy zaimportowano jej przestrzeń nazw. Bez właściwego using wygląda ona tak, jakby nie istniała, mimo że została skompilowana do zestawu.
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());
}
}Metody instancji są sprawdzane najpierw
Kompilator rozpoznaje wywołanie etapami: najpierw wyszukuje pasującą metodę instancji, w tym metody odziedziczone. Dopiero gdy żadna z nich nie pasuje, szuka metod rozszerzających.
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]);
}
}Preferowane są bliższe przestrzenie nazw
Jeśli pasują dwa rozszerzenia, wygrywa to, którego przestrzeń nazw znajduje się bliżej w hierarchii zagnieżdżenia. Niejednoznaczność, której kompilator nie może rozstrzygnąć, powoduje błąd kompilacji i wymaga jednoznacznego wskazania metody.
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());
}
}
}
}Usuwanie niejednoznaczności za pomocą wywołania statycznego
Gdy rozszerzenia kolidują ze sobą lub potrzebna jest jednoznaczność, należy bezpośrednio wywołać metodę statyczną, używając pełnej nazwy klasy. Pozwala to całkowicie pominąć rozpoznawanie metod rozszerzających.
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"));
}
}Rozwiązywanie przeciążeń wśród metod rozszerzających
Jeśli kilka metod rozszerzających ma tę samą nazwę, standardowe rozwiązywanie przeciążeń wybiera najlepiej pasującą metodę na podstawie typów parametrów, tak jak przy zwykłym przeciążaniu metod.
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());
}
}Wnioskowanie typów generycznych podczas rozpoznawania
W przypadku rozszerzeń generycznych kompilator wnioskuje argumenty typów na podstawie odbiorcy i argumentów. Jeśli wnioskowanie się nie powiedzie, dane rozszerzenie nie jest brane pod uwagę.
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());
}
}Globalne dyrektywy using i niejawne importy
Współczesny język C# umożliwia deklarowanie dyrektyw global using, dzięki czemu przestrzeń nazw jest dostępna w całym projekcie. Pozwala to udostępnić wspólne biblioteki metod rozszerzających w całym projekcie bez powtarzania importów.
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));
}
}Gdy typ odbiorcy ma znaczenie
Rozpoznawanie zależy od statycznego typu odbiorcy, a nie od jego typu w czasie wykonywania. Rozszerzenie zadeklarowane dla typu bazowego działa również wtedy, gdy zmienna przechowuje instancję typu pochodnego, chyba że dostępne jest rozszerzenie bardziej szczegółowe.
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());
}
}Model rozumowania przy rozpoznawaniu
Należy przyjąć następującą kolejność: (1) metody instancji, następnie (2) metody rozszerzające z zaimportowanych przestrzeni nazw, zaczynając od najbliższych, a potem (3) rozwiązywanie przeciążeń wśród kandydatów. Znajomość tej kolejności wyjaśnia niemal każdy zaskakujący wynik.
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());
}
}Wypróbuj samodzielnie
Proszę zdefiniować rozszerzenie w przestrzeni nazw, zaimportować je i wywołać. Następnie proszę wywołać tę samą metodę jawnie jako metodę statyczną, aby zobaczyć, że obie ścieżki działają.
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"));
}
}Szybkie sprawdzenie
Proszę zastanowić się, co sprawia, że metoda rozszerzająca jest widoczna.
Podsumowanie
Rozpoznawanie metod rozszerzających przebiega w jasno określonej kolejności.
- Najpierw sprawdzane są metody instancji; rozszerzenia uzupełniają tylko ich brak.
- Rozszerzenie jest widoczne tylko wtedy, gdy jego przestrzeń nazw jest dostępna za pomocą
usinglubglobal using. - Wygrywają bliższe przestrzenie nazw, a nierozstrzygnięte remisy powodują błędy kompilacji.
- Aby usunąć niejednoznaczność, zawsze można jawnie wywołać metodę statyczną.
- Rozpoznawanie wykorzystuje statyczny typ odbiorcy.
Często zadawane pytania
Czy lekcja „Rozstrzyganie metod rozszerzających” jest bezpłatna?
Tak — pełny tekst „Rozstrzyganie metod rozszerzających” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu C# Academy, przejdź na CoddyKit PRO. Kurs C# Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Rozstrzyganie metod rozszerzających”?
Poznaj sposób wyszukiwania rozszerzeń przez kompilator. Ćwiczysz C# Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć C# Academy?
Nie wymagamy żadnego doświadczenia. C# Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.
Ile czasu zajmuje lekcja „Rozstrzyganie metod rozszerzających”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji C# Academy?
Tak. Każda lekcja C# Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Definiowanie metod rozszerzających
- Rozszerzanie interfejsów i typów generycznych
- Rozstrzyganie metod rozszerzających
- Projektowanie dobrych rozszerzeń