次の方法で共有


拡張宣言 (C# リファレンス)

C# 14 以降では、最上位レベルの非ジェネリック static class 宣言では、 extension コンテナーを使用して 拡張メンバーを宣言できます。 拡張メンバーはメソッドまたはプロパティであり、インスタンスまたは静的メンバーと見なすことができます。 以前のバージョンの C# では、最上位の非ジェネリック静的クラスで宣言された静的 メソッド の最初のパラメーターに修飾子として this を追加することで拡張メソッドを有効にします。

extension ブロックは、拡張メンバーの型と受信者を指定します。 extension宣言内でメソッドとプロパティを宣言できます。 次の例では、インスタンス拡張メソッドとインスタンス プロパティを定義する 1 つの拡張ブロックを宣言します。

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;

同じ受信者を共有している限り、1 つのコンテナー内の任意の数のメンバーを宣言できます。 1 つのクラスで宣言できる拡張ブロックの数は 1 つだけです。 異なる拡張機能では、同じ型または受信者の名前を宣言する必要はありません。 唯一のメンバーが静的な場合、拡張パラメーターにはパラメーター名を含める必要はありません。

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;

重要

拡張機能では、メンバー宣言の スコープ は導入されません。 1 つのクラスで宣言されているすべてのメンバーは、複数の拡張内にある場合でも、一意のシグネチャを持つ必要があります。 生成されたシグネチャには、静的メンバーの名前に受信者の型と、拡張インスタンス メンバーのレシーバー パラメーターが含まれます。

次の例は、 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 宣言に型パラメーターを追加します。
  • 型が受信側で指定された型パラメーターと異なる場合は、型パラメーターをメンバー宣言に追加します。
  • 両方の場所で同じ型パラメーターを指定することはできません。

次の例は、2 つの拡張メンバーが 2 番目の型パラメーターを必要とする 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;
            }
        }
    }
}

メンバーAppendPrependは、変換のための追加の型パラメーターを指定します。 どのメンバーも、受信側の型パラメーターを繰り返しません。

同等の拡張メソッド宣言は、これらの型パラメーターのエンコード方法を示しています。

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# の構文と使用法の決定的なソースです。