次の方法で共有


メソッドのパラメーターと修飾子

既定では、C# は 値によって関数に引数を渡します。 この方法では、変数のコピーがメソッドに渡されます。 値 (struct) 型の場合、メソッドは のコピーを取得します。 参照 (class) 型の場合、メソッドは参照のコピーを取得 します。 パラメーター修飾子を使用して、 参照渡しで引数を渡すことができます。

構造体は 値型であるため、メソッドに値で構造体を渡すと、引数のコピーがメソッドに送信されます。 このメソッドは、このコピーで動作します。 メソッドは呼び出し元のメソッドの元の構造体にアクセスできないため、変更できません。 メソッドで変更できるのはコピーのみです。

クラス インスタンスは 参照型であり、値型ではありません。 メソッドに値で参照型を渡すと、メソッドはインスタンスへの参照のコピーを取得します。 どちらの変数も同じオブジェクトを参照します。 パラメーターは参照のコピーです。 呼び出されたメソッドは、呼び出し元メソッド内のインスタンスを再割り当てできません。 ただし、呼び出されたメソッドは、参照のコピーを使用してインスタンス メンバーにアクセスできます。 呼び出されたメソッドがインスタンス メンバーを変更した場合、呼び出し元メソッドは、同じインスタンスを参照しているため、それらの変更を認識します。

C# 言語リファレンスには、C# 言語の最新リリース バージョンが記載されています。 また、今後の言語リリースのパブリック プレビューの機能に関する初期ドキュメントも含まれています。

このドキュメントでは、言語の最後の 3 つのバージョンまたは現在のパブリック プレビューで最初に導入された機能を特定します。

ヒント

C# で機能が初めて導入された時期を確認するには、 C# 言語バージョン履歴に関する記事を参照してください。

値渡しと参照渡し

このセクションのすべての例では、次の 2 つの record 型を使用して、class 型と struct 型の違いを示します。

public record struct Point(int X, int Y);
// This doesn't use a primary constructor because the properties implemented for `record` types are 
// readonly in record class types. That would prevent the mutations necessary for this example.
public record class Point3D
{
    public int X { get; set; }
    public int Y { get; set; }
    public int Z { get; set; }
}

次の例の出力は、構造体型を値で渡すことと、クラス型を値で渡すことの違いを示しています。 どちらの Mutate メソッドも、引数のプロパティ値を変更します。 パラメーターが struct 型の場合、これらの変更は引数のデータのコピーに影響します。 パラメーターが class 型の場合、これらの変更は引数によって参照されるインスタンスに影響します。

public class PassTypesByValue
{
    public static void Mutate(Point pt)
    {
        Console.WriteLine($"\tEnter {nameof(Mutate)}:\t\t{pt}");
        pt.X = 19;
        pt.Y = 23;

        Console.WriteLine($"\tExit {nameof(Mutate)}:\t\t{pt}");
    }
    public static void Mutate(Point3D pt)
    {
        Console.WriteLine($"\tEnter {nameof(Mutate)}:\t\t{pt}");
        pt.X = 19;
        pt.Y = 23;
        pt.Z = 42;

        Console.WriteLine($"\tExit {nameof(Mutate)}:\t\t{pt}");
    }

    public static void TestPassTypesByValue()
    {
        Console.WriteLine("===== Value Types =====");

        var ptStruct = new Point { X = 1, Y = 2 };
        Console.WriteLine($"After initialization:\t\t{ptStruct}");

        Mutate(ptStruct);

        Console.WriteLine($"After called {nameof(Mutate)}:\t\t{ptStruct}");

        Console.WriteLine("===== Reference Types =====");

        var ptClass = new Point3D { X = 1, Y = 2, Z = 3 };

        Console.WriteLine($"After initialization:\t\t{ptClass}");

        Mutate(ptClass);
        Console.WriteLine($"After called {nameof(Mutate)}:\t\t{ptClass}");

        // Output:
        // ===== Value Types =====
        // After initialization:           Point { X = 1, Y = 2 }
        //         Enter Mutate:           Point { X = 1, Y = 2 }
        //         Exit Mutate:            Point { X = 19, Y = 23 }
        // After called Mutate:            Point { X = 1, Y = 2 }
        // ===== Reference Types =====
        // After initialization:           Point3D { X = 1, Y = 2, Z = 3 }
        //         Enter Mutate:           Point3D { X = 1, Y = 2, Z = 3 }
        //         Exit Mutate:            Point3D { X = 19, Y = 23, Z = 42 }
        // After called Mutate:            Point3D { X = 19, Y = 23, Z = 42 }
    }
}

修飾子は、参照 によって 引数をメソッドに渡す 1 つの方法です。 次のコードは、前の例をレプリケートしますが、パラメーターを参照渡しします。 struct 型に加えられた変更は、構造体が参照によって渡されるときに呼び出し元のメソッドに表示されます。 参照型が参照によって渡される場合、セマンティックな変更はありません。

public class PassTypesByReference
{
    public static void Mutate(ref Point pt)
    {
        Console.WriteLine($"\tEnter {nameof(Mutate)}:\t\t{pt}");
        pt.X = 19;
        pt.Y = 23;

        Console.WriteLine($"\tExit {nameof(Mutate)}:\t\t{pt}");
    }
    public static void Mutate(ref Point3D pt)
    {
        Console.WriteLine($"\tEnter {nameof(Mutate)}:\t\t{pt}");
        pt.X = 19;
        pt.Y = 23;
        pt.Z = 42;

        Console.WriteLine($"\tExit {nameof(Mutate)}:\t\t{pt}");
    }

    public static void TestPassTypesByReference()
    {
        Console.WriteLine("===== Value Types =====");

        var pStruct = new Point { X = 1, Y = 2 };
        Console.WriteLine($"After initialization:\t\t{pStruct}");

        Mutate(ref pStruct);

        Console.WriteLine($"After called {nameof(Mutate)}:\t\t{pStruct}");

        Console.WriteLine("===== Reference Types =====");

        var pClass = new Point3D { X = 1, Y = 2, Z = 3 };

        Console.WriteLine($"After initialization:\t\t{pClass}");

        Mutate(ref pClass);
        Console.WriteLine($"After called {nameof(Mutate)}:\t\t{pClass}");

        // Output:
        // ===== Value Types =====
        // After initialization:           Point { X = 1, Y = 2 }
        //         Enter Mutate:           Point { X = 1, Y = 2 }
        //         Exit Mutate:            Point { X = 19, Y = 23 }
        // After called Mutate:            Point { X = 19, Y = 23 }
        // ===== Reference Types =====
        // After initialization:           Point3D { X = 1, Y = 2, Z = 3 }
        //         Enter Mutate:           Point3D { X = 1, Y = 2, Z = 3 }
        //         Exit Mutate:            Point3D { X = 19, Y = 23, Z = 42 }
        // After called Mutate:            Point3D { X = 19, Y = 23, Z = 42 }
    }
}

前の例では、パラメーターのプロパティを変更しました。 メソッドは、パラメーターを新しい値に再割り当てすることもできます。 再割り当ては、値または参照によって渡される場合、構造体型とクラス型で動作が異なります。 次の例は、値渡しされるパラメーターが再割り当てされるときに構造体型とクラス型がどのように動作するかを示しています。

public class PassByValueReassignment
{
    public static void Reassign(Point pt)
    {
        Console.WriteLine($"\tEnter {nameof(Reassign)}:\t\t{pt}");
        pt = new Point { X = 13, Y = 29 };

        Console.WriteLine($"\tExit {nameof(Reassign)}:\t\t{pt}");
    }

    public static void Reassign(Point3D pt)
    {
        Console.WriteLine($"\tEnter {nameof(Reassign)}:\t\t{pt}");
        pt = new Point3D { X = 13, Y = 29, Z = -42 };

        Console.WriteLine($"\tExit {nameof(Reassign)}:\t\t{pt}");
    }

    public static void TestPassByValueReassignment()
    {
        Console.WriteLine("===== Value Types =====");

        var ptStruct = new Point { X = 1, Y = 2 };
        Console.WriteLine($"After initialization:\t\t{ptStruct}");

        Reassign(ptStruct);

        Console.WriteLine($"After called {nameof(Reassign)}:\t\t{ptStruct}");

        Console.WriteLine("===== Reference Types =====");

        var ptClass = new Point3D { X = 1, Y = 2, Z = 3 };

        Console.WriteLine($"After initialization:\t\t{ptClass}");

        Reassign(ptClass);
        Console.WriteLine($"After called {nameof(Reassign)}:\t\t{ptClass}");

        // Output:
        // ===== Value Types =====
        // After initialization:           Point { X = 1, Y = 2 }
        //         Enter Reassign:         Point { X = 1, Y = 2 }
        //         Exit Reassign:          Point { X = 13, Y = 29 }
        // After called Reassign:          Point { X = 1, Y = 2 }
        // ===== Reference Types =====
        // After initialization:           Point3D { X = 1, Y = 2, Z = 3 }
        //         Enter Reassign:         Point3D { X = 1, Y = 2, Z = 3 }
        //         Exit Reassign:          Point3D { X = 13, Y = 29, Z = -42 }
        // After called Reassign:          Point3D { X = 1, Y = 2, Z = 3 }
    }
}

前のサンプルは、パラメーターを新しい値に再割り当てするときに、その型が値型か参照型かに関係なく、呼び出し元のメソッドからその変更が表示されていないことを示しています。 次の例は、メソッドが参照によって受け取ったパラメーターを再割り当てするときの動作を示しています。

public class PassByReferenceReassignment
{
    public static void Reassign(ref Point pt)
    {
        Console.WriteLine($"\tEnter {nameof(Reassign)}:\t\t{pt}");
        pt = new Point { X = 13, Y = 29 };

        Console.WriteLine($"\tExit {nameof(Reassign)}:\t\t{pt}");
    }

    public static void Reassign(ref Point3D pt)
    {
        Console.WriteLine($"\tEnter {nameof(Reassign)}:\t\t{pt}");
        pt = new Point3D { X = 13, Y = 29, Z = -42 };

        Console.WriteLine($"\tExit {nameof(Reassign)}:\t\t{pt}");
    }

    public static void TestPassByReferenceReassignment()
    {
        Console.WriteLine("===== Value Types =====");

        var ptStruct = new Point { X = 1, Y = 2 };
        Console.WriteLine($"After initialization:\t\t{ptStruct}");

        Reassign(ref ptStruct);

        Console.WriteLine($"After called {nameof(Reassign)}:\t\t{ptStruct}");

        Console.WriteLine("===== Reference Types =====");

        var ptClass = new Point3D { X = 1, Y = 2, Z = 3 };

        Console.WriteLine($"After initialization:\t\t{ptClass}");

        Reassign(ref ptClass);
        Console.WriteLine($"After called {nameof(Reassign)}:\t\t{ptClass}");

        // Output:
        // ===== Value Types =====
        // After initialization:           Point { X = 1, Y = 2 }
        //         Enter Reassign:         Point { X = 1, Y = 2 }
        //         Exit Reassign:          Point { X = 13, Y = 29 }
        // After called Reassign:          Point { X = 13, Y = 29 }
        // ===== Reference Types =====
        // After initialization:           Point3D { X = 1, Y = 2, Z = 3 }
        //         Enter Reassign:         Point3D { X = 1, Y = 2, Z = 3 }
        //         Exit Reassign:          Point3D { X = 13, Y = 29, Z = -42 }
        // After called Reassign:          Point3D { X = 13, Y = 29, Z = -42 }
    }
}

前の例は、参照によって渡されるパラメーターの値を再割り当てする方法を、呼び出し元のコンテキストで表示する方法を示しています。

参照と値のセーフ コンテキスト

メソッドでは、パラメーターの値をフィールドに格納できます。 パラメーターを値渡しで渡す場合、通常は安全です。 メソッドは値をコピーし、メソッドがフィールドに格納すると参照型に到達できます。 パラメーターを参照渡しで安全に渡すには、コンパイラが新しい変数に参照を安全に代入するタイミングを定義する必要があります。 すべての式について、コンパイラは式または変数へのアクセスを制限する "セーフ コンテキスト" を定義します。 コンパイラは、safe-context と ref-safe-context という 2 つのスコープを使用します。

  • safe-context は、任意の式に安全にアクセスできるスコープを定義します。
  • ref-safe-context は、任意の式への "参照" に安全にアクセスしたり、変更したりできるスコープを定義します。

一般にこれらのスコープは、有効ではなくなった参照にコードがアクセスしたり、変更したりしないようにするためのメカニズムと考えることができます。 ある参照は、有効なオブジェクトまたは構造体を参照している限り有効です。 safe-context は、変数を代入または再代入できるタイミングを定義します。 ref-safe-context は、変数を ref 代入または ref 再代入できるタイミングを定義します。 代入により、変数が新しい値に割り当てられます。"ref 代入" では、変数を割り当てて別のストレージの場所を "参照" します。

参照パラメーター

値ではなく参照で引数を渡すには、パラメーター宣言で次のいずれかの修飾子を使用します。

  • ref: メソッドを呼び出す前に引数を初期化します。 メソッドはパラメーターに新しい値を割り当てることができますが、必須ではありません。
  • out: 呼び出し元のメソッドは、メソッドを呼び出す前に引数を初期化する必要はありません。 メソッドはパラメーターに値を割り当てる必要があります。
  • ref readonly: メソッドを呼び出す前に引数を初期化します。 メソッドはパラメーターに新しい値を割り当てることはできません。
  • in: メソッドを呼び出す前に引数を初期化します。 メソッドはパラメーターに新しい値を割り当てることはできません。 コンパイラは、in パラメーターへの引数のコピーを保持するための一時変数を作成する場合があります。

参照によって渡されるパラメーターは 参照変数です。 独自の値はありません。 代わりに、参照先と呼ばれる別の変数を 参照します。 参照変数を 再割り当て して参照先を変更することができます。

クラスのメンバーは、refref readonlyinout のみが異なるシグネチャを持つことはできません。 型の 2 つのメンバーの唯一の違いは、1 つのメンバーに ref パラメーターがあり、もう一方のメンバーに outref readonly、または in パラメーターがある場合に発生します。 ただし、次の例に示すように、1 つのメソッドに refref readonlyin、または out パラメーターがあり、もう 1 つのメソッドに値渡しされるパラメーターがある場合は、メソッドをオーバーロードできます。 非表示やオーバーライドなど、シグネチャの一致が必要な他の状況では、inrefref readonlyout はシグネチャの一部であり、互いに一致しません。

パラメーターに前述のいずれかの修飾子がある場合、対応する引数には互換性のある修飾子を指定できます。

  • ref パラメーターの引数には、ref 修飾子を含める必要があります。
  • out パラメーターの引数には、out 修飾子を含める必要があります。
  • in パラメーターの引数には、必要に応じて in 修飾子を含めることができます。 代わりにその引数で ref 修飾子が使用されている場合、コンパイラは警告を出します。
  • ref readonly パラメーターの引数には、in 修飾子または ref 修飾子を含める必要がありますが、両方を含める必要はありません。 どちらの修飾子も含まれていない場合、コンパイラは警告を出します。

これらの修飾子を使用すると、次のように引数の使用方法が説明されます。

  • ref は、メソッドが引数の値を読み取ったり書き込んだりできることを意味します。
  • out は、メソッドが引数の値を設定することを意味します。
  • ref readonly は、メソッドが引数の値を読み取りますが、書き込めないことを意味します。 引数は参照渡しで渡す必要があります。
  • in は、メソッドが引数の値を読み取りますが、書き込めないことを意味します。 引数は、参照または一時変数を介して渡されます。

次の種類のメソッドでは、前述のパラメーター修飾子を使用できません。

  • async 修飾子を使用して定義した Async メソッド。
  • yield return または yield break ステートメントを含む Iterator メソッド。

拡張メンバー には、次の引数キーワードの使用に関する制限もあります。

  • 拡張メソッドの最初の引数では、out キーワードを使用できません。
  • 引数が ref でない場合、または構造体として制約されていないジェネリック型の場合、拡張メソッドの最初の引数で struct キーワードを使用することはできません。
  • 最初の引数が ref readonly である場合を除き、in および struct キーワードは使用できません。
  • ジェネリック型では、構造体として制約されている場合であっても、ref readonly および in キーワードを使用することはできません。

プロパティは変数ではありません。 これらはメソッドです。 refパラメーターの引数としてプロパティを使用することはできません。

ref パラメーター修飾子

ref パラメーターを使用するには、メソッド定義と呼び出し元のメソッドの両方が、次の例に示すように ref キーワードを明示的に使用する必要があります。 (例外として、COM 呼び出しを行う場合は、呼び出し元のメソッドで ref を省略できます)。

void Method(ref int refArgument)
{
    refArgument = refArgument + 44;
}

int number = 1;
Method(ref number);
Console.WriteLine(number);
// Output: 45

引数を ref パラメーターに渡す前に、引数を初期化する必要があります。

out パラメーター修飾子

out パラメーターを使用するには、メソッド定義と呼び出し元のメソッドの両方で out キーワードを明示的に使用する必要があります。 次に例を示します。

int initializeInMethod;
OutArgExample(out initializeInMethod);
Console.WriteLine(initializeInMethod);     // value is now 44

void OutArgExample(out int number)
{
    number = 44;
}

メソッド呼び出しの前に、 out 引数として渡される変数を初期化する必要はありません。 ただし、呼び出されたメソッドは、戻る前に値を割り当てる必要があります。

Deconstruct メソッドは、複数の値を返すために、out 修飾子を使用してパラメーターを宣言します。 他のメソッドは、複数の戻り値の値タプルを返すことができます。

変数を別のステートメントで宣言してから、out 引数として渡すことができます。 out 変数を、別の変数宣言内ではなく、メソッド呼び出しの引数リスト内で宣言することもできます。 out 変数宣言により、よりコンパクトで読みやすいコードが生成されます。また、メソッド呼び出しの前に誤って変数に値を割り当てることもなくなります。 次の例は、number メソッドの呼び出しで 変数を定義しています。

string numberAsString = "1640";

if (Int32.TryParse(numberAsString, out int number))
    Console.WriteLine($"Converted '{numberAsString}' to {number}");
else
    Console.WriteLine($"Unable to convert '{numberAsString}'");
// The example displays the following output:
//       Converted '1640' to 1640

暗黙的に型指定されたローカル変数を宣言することもできます。

ref readonly 修飾子

メソッド宣言には、 ref readonly 修飾子が必要です。 呼び出しサイトの修飾子は省略可能です。 inまたはref修飾子を使用できます。 ref readonly 修飾子は、呼び出しサイトでは無効です。 呼び出しサイトで使用する修飾子は、引数の特性を記述するのに役立ちます。 引数が変数であり、書き込み可能な場合にのみ、 ref を使用できます。 inは、引数が変数の場合にのみ使用できます。 変数は書き込み可能または読み取り可能な場合があります。 引数が変数ではなく式である場合は、どちらの修飾子も追加できません。 次の例は、これらの条件を示しています。 次のメソッドでは、ref readonly 修飾子を使用して、パフォーマンス上の理由から、大きな構造体を参照渡しする必要があることを示しています。

public static void ForceByRef(ref readonly OptionStruct thing)
{
    // elided
}

refまたはin修飾子を使用して、メソッドを呼び出すことができます。 修飾子を省略すると、コンパイラは警告を出します。 引数が変数ではなく式の場合は、in または ref 修飾子を追加できないため、警告を抑制する必要があります。

ForceByRef(in options);
ForceByRef(ref options);
ForceByRef(options); // Warning! variable should be passed with `ref` or `in`
ForceByRef(new OptionStruct()); // Warning, but an expression, so no variable to reference

変数が readonly 変数の場合は、in 修飾子を使用する必要があります。 代わりに ref 修飾子を使用すると、コンパイラはエラーを出します。

ref readonly 修飾子は、引数が変数ではない式ではなく、変数であるとメソッドは想定している、ということを示します。 変数ではない式の例としては、定数、メソッドの戻り値、プロパティがあります。 引数が変数でない場合、コンパイラは警告を出します。

in パラメーター修飾子

メソッド宣言では in 修飾子が必要ですが、呼び出しサイトでは不要です。

var largeStruct = new LargeStruct { Value1 = 42, Value2 = 3.14, Value3 = "Hello" };

// Using 'in' avoids copying the large struct and prevents modification
ProcessLargeStruct(in largeStruct);
Console.WriteLine($"Original value unchanged: {largeStruct.Value1}");

// Without 'in', the struct would be copied (less efficient for large structs)
ProcessLargeStructByValue(largeStruct);
Console.WriteLine($"Original value still unchanged: {largeStruct.Value1}");

void ProcessLargeStruct(in LargeStruct data)
{
    // Can read the values
    Console.WriteLine($"Processing: {data.Value1}, {data.Value2}, {data.Value3}");
    
    // Uncomment the following line to see error CS8331
    // data.Value1 = 99; // Compilation error: cannot assign to 'in' parameter
}

void ProcessLargeStructByValue(LargeStruct data)
{
    // This method receives a copy of the struct
    Console.WriteLine($"Processing copy: {data.Value1}, {data.Value2}, {data.Value3}");
    
    // Modifying the copy doesn't affect the original
    data.Value1 = 99;
}

in修飾子を使用すると、コンパイラは引数の一時変数を作成し、その引数への読み取り専用参照を渡すことができます。 コンパイラは、引数を変換する必要がある場合、引数の型から暗黙的な変換がある場合、または引数が変数ではない値である場合は、常に一時変数を作成します。 たとえば、引数がリテラル値の場合や、プロパティ アクセサーから返される値の場合です。 API で引数を参照渡しする必要がある場合は、ref readonly 修飾子ではなく in 修飾子を選択します。

in パラメーターを使用してメソッドを定義することで、パフォーマンスの最適化を実現できます。 一部の struct 型引数のサイズが大きくなる可能性があり、厳密なループまたは重要なコード パスでメソッドを呼び出すと、それらの構造体をコピーするコストが大きくなります。 呼び出 in メソッドがその引数の状態を変更しないため、参照によって引数を安全に渡すことができることを指定するパラメーターを宣言します。 参照によりこれらの引数を渡して、(可能性がある) 高額なコピーを回避します。 呼び出しサイトで in 修飾子を明示的に追加して、引数が値ではなく、参照で渡されるようにします。 明示的に in を使用すると、次の 2 つの効果があります。

  • 呼び出しサイトで in を指定すると、コンパイラは、一致する in パラメーターで定義されたメソッドを強制的に選択します。 それ以外の場合は、2 つのメソッドで in の有無のみが異なるときは、値によるオーバーロードの方が適しています。
  • in を指定することで、参照渡しで引数を渡す意図を宣言します。 in で使用される引数では、直接参照できる場所を表す必要があります。 out および ref 引数と同じ一般ルールが適用されます。定数、通常のプロパティ、または値を生成するその他の式を使用することはできません。 それ以外の場合は、呼び出しサイトで in を省略すると、メソッドに読み取り専用の参照渡しで渡すために、一時変数を作成しても問題ないことが、コンパイラに通知されます。 コンパイラでは、一時変数を作成して、in 引数でのいくつかの制限に対処します。
    • 一時変数では、in パラメーターとしてコンパイル時の定数を許可します。
    • 一時変数では、プロパティ、または in パラメーターのその他の式を許可します。
    • 一時変数では、引数の型からパラメーターの型への暗黙の変換がある引数を許可します。

先のすべてのインスタンスで、コンパイラは定数、プロパティ、またはその他の式の値を格納する一時変数を作成します。

これらのルールが発生するコード例を次に示します。

static void Method(in int argument)
{
    // implementation removed
}

Method(5); // OK, temporary variable created.
Method(5L); // CS1503: no implicit conversion from long to int
short s = 0;
Method(s); // OK, temporary int created with the value 0
Method(in s); // CS1503: cannot convert from in short to in int
int i = 42;
Method(i); // passed by readonly reference
Method(in i); // passed by readonly reference, explicitly using `in`

ここでは、値渡しの引数を使用する別のメソッドが使用できたと想定します。 次のコードに示すように、結果が変更されます。

static void Method(int argument)
{
    // implementation removed
}

static void Method(in int argument)
{
    // implementation removed
}

Method(5); // Calls overload passed by value
Method(5L); // CS1503: no implicit conversion from long to int
short s = 0;
Method(s); // Calls overload passed by value.
Method(in s); // CS1503: cannot convert from in short to in int
int i = 42;
Method(i); // Calls overload passed by value
Method(in i); // passed by readonly reference, explicitly using `in`

引数が参照で渡されるメソッドの呼び出しは、最後のメソッドのみです。

注意

先のコードでは、わかりやすくするために int を引数型として使用します。 intは、ほとんどの最新のマシンでは参照より大きくないため、単一のintを読み取り専用参照として渡してもメリットはありません。

params 修飾子

params キーワードを持つパラメーターは、メソッド宣言の最後のパラメーターである必要があります。 メソッド宣言で使用できる params キーワードは 1 つだけです。

params パラメーターはコレクション型として宣言する必要があります。 認識されるコレクションの種類は次のとおりです。

C# 13 より前は、パラメーターに 1 次元配列を使用する必要があります。

params パラメーターを使用してメソッドを呼び出す場合は、以下を渡すことができます。

  • 配列要素の型の引数のコンマ区切りのリスト。
  • 指定した型の引数のコレクション。
  • 引数なし。 引数を渡さない場合、params リストの長さはゼロになります。

次の例では、引数を params パラメーターに送信するさまざまな方法を示します。

public static void ParamsModifierExample(params int[] list)
{
    for (int i = 0; i < list.Length; i++)
    {
        System.Console.Write(list[i] + " ");
    }
    System.Console.WriteLine();
}

public static void ParamsModifierObjectExample(params object[] list)
{
    for (int i = 0; i < list.Length; i++)
    {
        System.Console.Write(list[i] + " ");
    }
    System.Console.WriteLine();
}

public static void TryParamsCalls()
{
    // You can send a comma-separated list of arguments of the
    // specified type.
    ParamsModifierExample(1, 2, 3, 4);
    ParamsModifierObjectExample(1, 'a', "test");

    // A params parameter accepts zero or more arguments.
    // The following calling statement displays only a blank line.
    ParamsModifierObjectExample();

    // An array argument can be passed, as long as the array
    // type matches the parameter type of the method being called.
    int[] myIntArray = { 5, 6, 7, 8, 9 };
    ParamsModifierExample(myIntArray);

    object[] myObjArray = { 2, 'b', "test", "again" };
    ParamsModifierObjectExample(myObjArray);

    // The following call causes a compiler error because the object
    // array cannot be converted into an integer array.
    //ParamsModifierExample(myObjArray);

    // The following call does not cause an error, but the entire
    // integer array becomes the first element of the params array.
    ParamsModifierObjectExample(myIntArray);
}
/*
Output:
    1 2 3 4
    1 a test

    5 6 7 8 9
    2 b test again
    System.Int32[]
*/

オーバーロードの解決により、 params パラメーターの引数がコレクション型である場合、あいまいさが発生する可能性があります。 引数のコレクション型は、パラメーターのコレクション型に変換できる必要があります。 異なるオーバーロードによってそのパラメーターの変換が向上する場合、そのメソッドの方が適している可能性があります。 ただし、 params パラメーターの引数が不連続要素または不足している場合、 params パラメーター型が異なるすべてのオーバーロードは、そのパラメーターに対して等しくなります。

詳細については、「C# 言語仕様」引数リストのセクションを参照してください。 言語仕様は、C# の構文と使用法に関する信頼性のある情報源です。