Sdílet prostřednictvím


Deklarace rozšíření (Referenční dokumentace jazyka C#)

Počínaje jazykem C# 14 mohou negenerické, nejvyšší úrovně static class deklarace za použití extension kontejnerů deklarovat členy rozšíření. Členy rozšíření jsou metody nebo vlastnosti a mohou se zdát, že jde o instance nebo statické členy. Starší verze jazyka C# umožňují rozšiřující metody přidáním this jako modifikátoru do prvního parametru statické metody deklarované v negenerické statické třídě nejvyšší úrovně.

Blok extension určuje typ a přijímač pro členy rozšíření. Můžete deklarovat metody a vlastnosti uvnitř extension deklarace. Následující příklad deklaruje jeden blok rozšíření, který definuje metodu rozšíření instance a vlastnost instance.

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 definuje přijímač: sequence, což je IEnumerable<int>. Typ přijímače může být negenerický, otevřený obecný nebo uzavřený obecný typ. Název sequence je dostupný ve všech deklarovaných členech instance v rámci tohoto rozšíření. Metoda rozšíření i vlastnost obě přistupují k sequence.

K libovolnému členu rozšíření lze přistupovat, jako by se jednalo o členy typu příjemce:

IEnumerable<int> numbers = Enumerable.Range(1, 10);
numbers = numbers.AddValue(10);

var median = numbers.Median;

Můžete deklarovat libovolný počet členů v jednom kontejneru, pokud sdílejí stejný příjemce. Můžete deklarovat tolik rozšiřujících bloků, kolik chcete, v jedné třídě. Různá rozšíření nemusí deklarovat stejný typ nebo název příjemce. Parametr rozšíření nemusí obsahovat název parametru, pokud jsou pouze statické členy:

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>();
}

Statická rozšíření se dají volat, jako by se jedná o statické členy typu příjemce:

var newSequence = IEnumerable<int>.Generate(5, 10, 2);
var identity = IEnumerable<int>.Identity;

Důležité

Rozšíření nezavádí obor pro deklarace členů. Všichni členové deklarovaní v jedné třídě, i když ve více rozšířeních, musí mít jedinečné podpisy. Vygenerovaný podpis obsahuje typ příjemce v jeho názvu pro statické členy a parametr příjemce pro členy instance rozšíření.

Následující příklad ukazuje rozšiřující metodu this používající modifikátor:

public static class NumericSequenceExtensionMethods
{
    public static IEnumerable<int> AddValue(this IEnumerable<int> sequence, int operand)
    {
        foreach (var item in sequence)
            yield return item + operand;
    }
}

Metodu Add lze volat z jakékoli jiné metody, jako by byla členem IEnumerable<int> rozhraní:

IEnumerable<int> numbers = Enumerable.Range(1, 10);
numbers = numbers.AddValue(10);

Obě formy rozšiřujících metod generují stejný zprostředkující jazyk (IL). Volající nemůžou rozlišovat mezi nimi. Ve skutečnosti můžete stávající metody rozšíření převést na novou syntaxi člena bez zásadní změny. Formáty jsou kompatibilní jak ve formátu binárním, tak zdrojovém.

Obecné bloky rozšíření

Kde zadáte parametry typu člena rozšíření deklarovaného v bloku rozšíření, závisí na tom, kde je tento parametr typu povinný:

  • Parametr typu přidáte do extension deklarace při použití parametru typu v přijímači.
  • Parametr typu přidáte do deklarace členu, pokud se typ liší od jakéhokoli parametru typu zadaného v přijímači.
  • Na obou místech nemůžete zadat stejný typ parametru.

Následující příklad ukazuje blok rozšíření pro IEnumerable<T>, ve kterém dva členové rozšíření vyžadují druhý typový parametr.

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;
            }
        }
    }
}

Členové Append a Prepend určují dodatečný parametr typu pro převod. Žádný člen neopakuje parametr typu příjemce.

Deklarace ekvivalentní metody rozšíření ukazují, jak jsou tyto parametry typu kódovány:

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;
        }
    }
}

Viz také

Specifikace jazyka C#

Další informace najdete ve specifikaci jazyka C#. Specifikace jazyka je konečným zdrojem syntaxe a použití jazyka C#.