Partager via


Déclaration d’extension (référence C#)

À partir de C# 14, les déclarations static class non génériques de niveau supérieur peuvent utiliser des conteneurs extension pour déclarer des membres d'extension. Les membres d’extension sont des méthodes ou des propriétés et peuvent apparaître comme des membres d’instance ou statiques. Les versions antérieures de C# activent les méthodes d’extension en ajoutant this en tant que modificateur au premier paramètre d’une méthode statique déclarée dans une classe statique de niveau supérieur et non générique.

Le extension bloc spécifie le type et le récepteur pour les membres d’extension. Vous pouvez déclarer des méthodes et des propriétés à l’intérieur de la extension déclaration. L’exemple suivant déclare un bloc d’extension unique qui définit une méthode d’extension d’instance et une propriété d’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();
    }
}

Le extension définit le récepteur : sequence, qui est un IEnumerable<int>. Le type de récepteur peut être non générique, générique ouvert ou un type générique fermé. Le nom sequence est accessible dans toutes les instances déclarées dans cette extension. La méthode d'extension et la propriété accèdent toutes deux à sequence.

Tous les membres de l’extension sont accessibles comme s’ils étaient membres du type de récepteur :

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

var median = numbers.Median;

Vous pouvez déclarer n’importe quel nombre de membres dans un seul conteneur, tant qu’ils partagent le même récepteur. Vous pouvez également déclarer autant de blocs d’extension dans une seule classe. Différentes extensions n’ont pas besoin de déclarer le même type ou le même nom de récepteur. Le paramètre d’extension n’a pas besoin d’inclure le nom du paramètre si les seuls membres sont statiques :

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

Les extensions statiques peuvent être appelées comme si elles sont des membres statiques du type de récepteur :

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

Importante

Une extension n'introduit pas d'étendue pour les déclarations de membres. Tous les membres déclarés dans une classe unique, même dans plusieurs extensions, doivent avoir des signatures uniques. La signature générée inclut le type de récepteur dans son nom pour les membres statiques et le paramètre de récepteur pour les membres de l’instance d’extension.

L’exemple suivant montre une méthode d’extension à l’aide du this modificateur :

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

La Add méthode peut être appelée à partir de n’importe quelle autre méthode comme si elle était membre de l’interface IEnumerable<int> :

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

Les deux formes de méthodes d’extension génèrent le même langage intermédiaire (IL). Les appelants ne peuvent pas faire de distinction entre eux. En réalité, vous pouvez convertir les méthodes d'extension existantes vers la nouvelle syntaxe de membre sans changement cassant. Les formats sont à la fois binaires et compatibles avec la source.

Blocs d’extension génériques

Lorsque vous spécifiez les paramètres de type d’un membre d’extension déclaré dans un bloc d’extension dépend de l’endroit où ce paramètre de type est requis :

  • Vous ajoutez le paramètre de type à la extension déclaration lorsque le paramètre de type est utilisé dans le récepteur.
  • Vous ajoutez le paramètre de type à la déclaration de membre lorsque le type est distinct de n’importe quel paramètre de type spécifié sur le récepteur.
  • Vous ne pouvez pas spécifier le même paramètre de type dans les deux emplacements.

L’exemple suivant montre un bloc d’extension pour IEnumerable<T> lequel deux des membres de l’extension nécessitent un deuxième paramètre de type :

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

Les membres Append et Prepend spécifient le paramètre de type supplémentaire pour la conversion. Aucun des membres ne répète le paramètre de type pour le récepteur.

Les déclarations de méthode d’extension équivalentes montrent comment ces paramètres de type sont encodés :

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

Voir aussi

Spécification du langage C#

Pour plus d'informations, voir la spécification du langage C#. La spécification du langage est la source de référence pour la syntaxe C# et son utilisation.