Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Począwszy od języka C# 14, najwyższego zakresu deklaracje niegeneryczne static class
mogą używać extension
kontenerów do deklarowania członków rozszerzenia. Członkowie rozszerzenia są metodami lub właściwościami i mogą być członkami instancji lub członkami statycznymi. Wcześniejsze wersje języka C# umożliwiają stosowanie metod rozszerzeń przez dodanie this
jako modyfikatora do pierwszego parametru metody statycznej zadeklarowanej w klasie statycznej najwyższego poziomu.
Blok extension
określa typ i odbiornik dla elementów rozszerzeń. Metody i właściwości można zadeklarować wewnątrz deklaracji extension
. Poniższy przykład deklaruje pojedynczy blok rozszerzenia, który definiuje metodę rozszerzającą dla instancji oraz umożliwia dodanie właściwości instancji.
public static class NumericSequences
{
extension(IEnumerable<int> sequence)
{
public IEnumerable<int> AddValue(int operand)
{
foreach (var item in sequence)
{
yield return item + operand;
}
}
public int Median
{
get
{
var sortedList = sequence.OrderBy(n => n).ToList();
int count = sortedList.Count;
int middleIndex = count / 2;
if (count % 2 == 0)
{
// Even number of elements: average the two middle elements
return (sortedList[middleIndex - 1] + sortedList[middleIndex]);
}
else
{
// Odd number of elements: return the middle element
return sortedList[middleIndex];
}
}
}
public int this[int index] => sequence.Skip(index).First();
}
}
Element extension
definiuje odbiornik: sequence
, czyli IEnumerable<int>
. Typ odbiornika może być niesparametryzowany, otwarty generyczny lub zamknięty generyczny. Nazwa sequence
jest w zakresie we wszystkich elementach członkowskich instancji, które są zadeklarowane w tym rozszerzeniu. Zarówno metoda rozszerzenia, jak i właściwość, mają dostęp do sequence
.
Do każdego z członków rozszerzenia można uzyskać dostęp, jak gdyby to były członkowie typu odbiornika.
IEnumerable<int> numbers = Enumerable.Range(1, 10);
numbers = numbers.AddValue(10);
var median = numbers.Median;
Można zadeklarować dowolną liczbę elementów w jednym kontenerze, o ile dzielą ten sam odbiornik. Można również zadeklarować dowolną liczbę bloków rozszerzeń w jednej klasie. Różne rozszerzenia nie muszą deklarować tego samego typu ani nazwy odbiorcy. Parametr rozszerzenia nie musi zawierać nazwy parametru, jeśli jedynymi elementami członkowskimi są statyczne:
extension(IEnumerable<int>)
{
// Method:
public static IEnumerable<int> Generate(int low, int count, int increment)
{
for (int i = 0; i < count; i++)
yield return low + (i * increment);
}
// Property:
public static IEnumerable<int> Identity => Enumerable.Empty<int>();
}
Rozszerzenia statyczne mogą być wywoływane tak, jak gdyby były statycznymi członkami typu odbiorcy.
var newSequence = IEnumerable<int>.Generate(5, 10, 2);
var identity = IEnumerable<int>.Identity;
Ważne
Rozszerzenie nie wprowadza zakresu dla deklaracji członków. Wszyscy członkowie zadeklarowani w jednej klasie, nawet jeśli są w różnych rozszerzeniach, muszą mieć unikatowe sygnatury. Wygenerowany podpis zawiera typ odbiorcy w nazwie statycznych członków oraz parametr odbiornika dla członków instancji rozszerzenia.
W poniższym przykładzie przedstawiono metodę rozszerzenia przy użyciu this
modyfikatora:
public static class NumericSequenceExtensionMethods
{
public static IEnumerable<int> AddValue(this IEnumerable<int> sequence, int operand)
{
foreach (var item in sequence)
yield return item + operand;
}
}
Metodę Add
można wywołać z dowolnej innej metody, tak jakby była elementem członkowskim interfejsu IEnumerable<int>
:
IEnumerable<int> numbers = Enumerable.Range(1, 10);
numbers = numbers.AddValue(10);
Obie formy metod rozszerzeń generują ten sam język pośredni (IL). Osoby wywołujące nie mogą dokonać rozróżnienia między nimi. W rzeczywistości można przekonwertować istniejące metody rozszerzenia na nową składnię składową bez zmiany powodującej niezgodność. Formaty są zarówno binarne, jak i źródłowe.
Ogólne bloki rozszerzeń
W przypadku określenia parametrów typu dla elementu członkowskiego rozszerzenia zadeklarowanego w bloku rozszerzenia zależy od tego, gdzie jest wymagany ten parametr typu:
- Należy dodać parametr typu do deklaracji
extension
, gdy parametr typu jest używany w odbiorniku. - Należy dodać parametr typu do deklaracji członka, gdy typ różni się od dowolnego parametru typu podanego w odbiorniku.
- Nie można określić tego samego parametru typu w obu lokalizacjach.
W poniższym przykładzie pokazano blok rozszerzenia, dla IEnumerable<T>
którego dwa elementy członkowskie rozszerzenia wymagają drugiego parametru typu:
public static class GenericExtensions
{
extension<TReceiver>(IEnumerable<TReceiver> source)
{
public IEnumerable<TReceiver> Spread(int start, int count)
=> source.Skip(start).Take(count);
public IEnumerable<TReceiver> Append<TArg>(IEnumerable<TArg> second, Func<TArg, TReceiver> Converter)
{
foreach(TReceiver item in source)
{
yield return item;
}
foreach (TArg item in second)
{
yield return Converter(item);
}
}
public IEnumerable<TReceiver> Prepend<TArg>(IEnumerable<TArg> second, Func<TArg, TReceiver> Converter)
{
foreach (TArg item in second)
{
yield return Converter(item);
}
foreach (TReceiver item in source)
{
yield return item;
}
}
}
}
Składowe Append
i Prepend
określają dodatkowy parametr typu dla konwersji. Żaden z elementów członkowskich nie powtarza parametru typu dla odbiornika.
Równoważne deklaracje metody rozszerzenia pokazują, jak te parametry typu są kodowane:
public static class GenericExtensions
{
public static IEnumerable<T> Spread<T>(this IEnumerable<T> source, int start, int count)
=> source.Skip(start).Take(count);
public static IEnumerable<T1> Append<T1, T2>(this IEnumerable<T1> source, IEnumerable<T2> second, Func<T2, T1> Converter)
{
foreach (T1 item in source)
{
yield return item;
}
foreach (T2 item in second)
{
yield return Converter(item);
}
}
public static IEnumerable<T1> Prepend<T1, T2>(this IEnumerable<T1> source, IEnumerable<T2> second, Func<T2, T1> Converter)
{
foreach (T2 item in second)
{
yield return Converter(item);
}
foreach (T1 item in source)
{
yield return item;
}
}
}
Zobacz także
Specyfikacja języka C#
Aby uzyskać więcej informacji, zobacz Specyfikacja języka C#. Specyfikacja języka jest ostatecznym źródłem informacji o składni i użyciu języka C#.