Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Начиная с C# 14, верхнего уровня, негенерические static class
объявления могут использовать extension
контейнеры для объявления членов расширения. Члены расширения являются методами или свойствами и могут быть экземплярами или статическими элементами. Более ранние версии C# позволяют использовать методы расширения путем добавления this
в качестве модификатора к первому параметру статического метода, объявленного в негенерическом статичном классе верхнего уровня.
Блок extension
задает тип и приемник для членов расширения. Методы и свойства можно объявлять внутри extension
объявления. В следующем примере объявляется один блок расширения, определяющий метод расширения экземпляра и свойство экземпляра.
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();
}
}
Определенный extension
определяет приемник: sequence
, который является IEnumerable<int>
. Тип приемника может быть негенерным, открытым универсальным или закрытым универсальным типом. Имя sequence
находится в области каждого элемента экземпляра, объявленного в этом расширении. Метод расширения и свойство оба имеют доступ к sequence
.
Доступ к любому члену расширения можно получить, как если бы они были членами типа приемника:
IEnumerable<int> numbers = Enumerable.Range(1, 10);
numbers = numbers.AddValue(10);
var median = numbers.Median;
Вы можете объявить любое количество участников в одном контейнере, если они делят один и тот же приемник. Кроме того, можно объявить столько блоков расширения в одном классе. Разные расширения не обязаны объявлять одинаковый тип или название получателя. Параметр расширения не должен включать имя параметра, если только члены являются статическими:
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>();
}
Статические расширения можно вызывать так, как будто они являются статическими элементами типа приемника:
var newSequence = IEnumerable<int>.Generate(5, 10, 2);
var identity = IEnumerable<int>.Identity;
Это важно
Расширение не создает область в объявлениях членов. Все члены, объявленные в одном классе, даже если в нескольких расширениях, должны иметь уникальные сигнатуры. Созданная сигнатура включает тип приемника в название для статических элементов и параметр приемника для членов экземпляра для расширений.
В следующем примере показан метод расширения с помощью this
модификатора:
public static class NumericSequenceExtensionMethods
{
public static IEnumerable<int> AddValue(this IEnumerable<int> sequence, int operand)
{
foreach (var item in sequence)
yield return item + operand;
}
}
Метод Add
можно вызвать из любого другого IEnumerable<int>
метода, как если бы он был членом интерфейса:
IEnumerable<int> numbers = Enumerable.Range(1, 10);
numbers = numbers.AddValue(10);
Обе формы методов расширения создают один и тот же промежуточный язык (IL). Вызывающие не могут различать их. Фактически можно преобразовать существующие методы расширения в новый синтаксис члена без критического изменения. Форматы являются совместимыми как на двоичном уровне, так и на уровне исходного кода.
Универсальные блоки расширений
Где вы указали параметры типа для члена расширения, объявленного в блоке расширения, зависит от того, где они требуются.
- Вы добавляете параметр типа в объявление
extension
, когда параметр типа используется в получателе. - Параметр типа добавляется в объявление члена, если тип отличается от любого параметра типа, указанного для приемника.
- Нельзя указать один и тот же параметр типа в обоих местах.
В следующем примере показан блок расширения, для IEnumerable<T>
которого два члена расширения требуют второго параметра типа:
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;
}
}
}
}
Члены Append
и Prepend
определяют параметр дополнительного типа для преобразования. Ни один из членов не повторяет параметр типа для получателя.
Эквивалентные объявления метода расширения показывают, как эти параметры типа кодируются:
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;
}
}
}
См. также
Спецификация языка C#
Дополнительные сведения см. в спецификации языка C#. Спецификация языка является авторитетным источником синтаксиса и использования языка C#.