다음을 통해 공유


메서드 매개 변수 및 한정자

기본적으로 C#의 인수는 값으로 함수에 전달됩니다. 이는 변수의 복사본이 메서드에 전달된다는 의미입니다. 값(struct) 형식의 경우 의 복사본이 메서드에 전달됩니다. 참조(class) 형식의 경우 참조의 복사본이 메서드에 전달됩니다. 매개 변수 한정자를 사용하면 참조로 인수를 전달할 수 있습니다.

구조체는값 형식이므로 메서드에 값으로 구조체를 전달할 때 인수의 복사본을 받고 작동합니다. 메서드가 호출 메서드의 원래 구조체에 액세스할 수 없으므로 어떤 방식으로든 변경할 수 없습니다. 메서드는 복사본만 변경할 수 있습니다.

클래스 인스턴스는 값 형식이 아니라 참조 형식입니다. 참조 형식이 값으로 메서드에 전달되면 메서드는 인스턴스에 대한 참조의 복사본을 받습니다. 두 변수 모두 동일한 개체를 참조하세요. 매개 변수는 참조의 복사본입니다. 호출된 메서드는 호출 메서드의 인스턴스를 다시 할당할 수 없습니다. 그러나 호출된 메서드는 참조 복사본을 사용하여 인스턴스 멤버에 액세스할 수 있습니다. 호출된 메서드가 인스턴스 멤버를 변경하는 경우 호출 메서드도 동일한 인스턴스를 참조하므로 해당 변경 내용을 확인합니다.

값에 의한 전달 및 참조에 의한 전달

이 섹션의 모든 예제에서는 다음 두 가지 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 }
    }
}

한정자는 메서드에 참조 인수를 전달하는 한 가지 방법입니다. 다음 코드는 앞의 예제를 따르지만 참조로 매개 변수를 전달합니다. 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-contextref-safe-context라는 두 가지 범위를 사용합니다.

  • safe-context는 모든 식에 안전하게 액세스할 수 있는 범위를 정의합니다.
  • ref-safe-context는 식에 대한 참조가 안전하게 액세스되거나 수정될 수 있는 범위를 정의합니다.

비공식적으로 이러한 범위를 코드가 더 이상 유효하지 않은 참조를 액세스하거나 수정하지 않도록 하는 메커니즘으로 생각할 수 있습니다. 참조는 유효한 개체 또는 구조체를 참조하는 한 유효합니다. safe-context는 변수가 할당되거나 다시 할당될 수 있는 시기를 정의합니다. ref-safe-context는 변수가 참조 할당됨 또는 참조 재할당됨될 수 있는 시기를 정의합니다. 할당은 변수를 새 값에 할당합니다. 참조 할당은 다른 스토리지 위치를 참조할 변수를 할당합니다.

참조 매개 변수

값 대신 참조로 인수를 전달하려면 매개 변수 선언에 다음 한정자 중 하나를 적용합니다.

  • ref: 메서드를 호출하기 전에 인수를 초기화해야 합니다. 메서드는 매개 변수에 새 값을 할당할 수 있지만 반드시 그렇게 할 필요는 없습니다.
  • out: 호출 메서드는 메서드를 호출하기 전에 인수를 초기화할 필요가 없습니다. 메서드는 매개 변수에 값을 할당해야 합니다.
  • ref readonly: 메서드를 호출하기 전에 인수를 초기화해야 합니다. 메서드는 매개 변수에 새 값을 할당할 수 없습니다.
  • in: 메서드를 호출하기 전에 인수를 초기화해야 합니다. 메서드는 매개 변수에 새 값을 할당할 수 없습니다. 컴파일러는 in 매개 변수에 대한 인수 복사본을 보관하기 위해 임시 변수를 만들 수 있습니다.

참조로 전달되는 매개 변수는참조 변수입니다. 그것은 자신의 가치를 가지고 있지 않습니다. 대신 참조다른 변수를 참조합니다. 참조 변수는 참조를 변경하는 참조다시 할당된 수 있습니다.

클래스의 멤버는 ref, ref readonly, in 또는 out만 다른 서명을 포함할 수 없습니다. 특정 형식의 두 멤버가 하나는 ref 매개 변수를 포함하고 다른 하나는 out, ref readonly 또는 in 매개 변수를 포함한다는 것 외에는 차이가 없으면 컴파일러 오류가 발생합니다. 그러나 다음 예제에 나와 있는 것처럼 메서드 하나에는 ref, ref readonly, in 또는 out 매개 변수가 포함되어 있고 다른 하나에는 값으로 전달되는 매개 변수가 포함되어 있으면 메서드를 오버로드할 수 있습니다. 숨기기나 재정의와 같이 서명이 일치해야 하는 다른 상황에서는 in, ref, ref readonlyout이 서명의 일부가 되며 서로 일치하지 않습니다.

매개 변수에 이전 한정자 중 하나가 있는 경우 해당 인수는 호환 가능한 한정자를 가질 수 있습니다.

  • ref 매개 변수의 인수에는 ref 한정자가 포함되어야 합니다.
  • out 매개 변수의 인수에는 out 한정자가 포함되어야 합니다.
  • in 매개 변수의 인수는 선택적으로 in 한정자를 포함할 수 있습니다. 대신 ref 한정자를 인수에 사용하면 컴파일러가 경고를 발급합니다.
  • ref readonly 매개 변수의 인수에는 in 또는 ref 한정자가 포함되어야 하지만 둘 다 포함되어서는 안 됩니다. 두 한정자가 모두 포함되어 있지 않으면 컴파일러는 경고를 발급합니다.

이러한 한정자를 사용하면 인수가 사용되는 방법을 설명합니다.

  • ref는 메서드가 인수 값을 읽거나 쓸 수 있음을 의미합니다.
  • out은 메서드가 인수 값을 설정함을 의미합니다.
  • ref readonly는 메서드가 읽기는 하지만 인수 값을 쓸 수 없음을 의미합니다. 인수는 참조로 전달되어야 합니다.
  • in는 메서드가 읽기는 하지만 인수 값을 쓸 수 없음을 의미합니다. 인수는 참조로 전달되거나 임시 변수를 통해 전달됩니다.

다음 종류의 메서드에서는 이전 매개 변수 한정자를 사용할 수 없습니다.

  • async 한정자를 사용하여 정의하는 비동기 메서드
  • yield return 또는 yield break 문을 포함하는 반복기 메서드

확장 메서드에는 다음 인수 키워드 사용에 대한 제한 사항도 있습니다.

  • 확장 메서드의 첫 번째 인수에는 out 키워드를 사용할 수 없습니다.
  • 인수가 ref가 아니거나 구조체로 제한되지 않는 제네릭 형식인 경우 확장 메서드의 첫 번째 인수에 struct 키워드를 사용할 수 없습니다.
  • 첫 번째 인수가 ref readonly가 아니면 instruct 키워드를 사용할 수 없습니다.
  • ref readonlyin 키워드는 구조체로 제한되는 경우에도 제네릭 형식에 사용할 수 없습니다.

속성은 변수가 아닙니다. 메서드입니다. 속성은 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 한정자는 메서드 선언에 필요하지만 호출 사이트에서는 필요하지 않습니다.

int readonlyArgument = 44;
InArgExample(readonlyArgument);
Console.WriteLine(readonlyArgument);     // value is still 44

void InArgExample(in int number)
{
    // Uncomment the following line to see error CS8331
    //number = 19;
}

in 한정자를 사용하면 컴파일러가 인수에 대한 임시 변수를 만들고 해당 인수에 대한 읽기 전용 참조를 전달할 수 있습니다. 인수를 변환해야 할 때, 인수 형식에서 암시적 변환이 있을 때 또는 인수가 변수가 아닌 값일 때 컴파일러는 항상 임시 변수를 만듭니다. 예를 들어, 인수가 리터럴 값이거나 속성 접근자에서 반환된 값인 경우입니다. API에서 참조로 인수를 전달해야 하는 경우 ref readonly 한정자 대신 in 한정자를 선택합니다.

in 매개 변수를 사용하여 정의된 메서드는 잠재적으로 성능 최적화를 얻습니다. 일부 struct 형식 인수는 크기가 클 수 있으며 긴밀한 루프 또는 중요한 코드 경로에서 메서드가 호출되는 경우 해당 구조를 복사하는 데 드는 비용이 상당합니다. 메서드는 호출된 메서드가 해당 인수의 상태를 수정하지 않기 때문에 인수가 참조로 안전하게 전달될 수 있음을 지정하기 위해 in 매개 변수를 선언합니다. 이러한 인수를 참조로 전달하면 (잠재적으로) 비용이 많이 드는 복사본을 방지할 수 있습니다. 호출 사이트에서 in 한정자를 명시적으로 추가하여 인수가 값이 아닌 참조로 전달되도록 합니다. 명시적으로 in을 사용하는 경우 다음과 같은 두 가지 효과가 있습니다.

  • 호출 사이트에서 in을 지정하면 컴파일러가 일치하는 in 매개 변수로 정의된 메서드를 선택하게 됩니다. 그렇지 않으면 두 메서드가 in이 있을 때만 다른 경우 by 값 오버로드가 더 적합합니다.
  • in을 지정하면 인수를 참조로 전달하려는 의도를 선언하는 것입니다. in에 사용된 인수는 직접 참조할 수 있는 위치를 나타내야 합니다. outref 인수에는 동일한 일반 규칙이 적용됩니다. 상수, 일반 속성 또는 값을 생성하는 다른 식은 사용할 수 없습니다. 그렇지 않은 경우 호출 사이트에서 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`

이제 by 값 인수를 사용하는 다른 메서드를 사용할 수 있다고 가정하겠습니다. 결과는 다음 코드와 같이 변경됩니다.

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 키워드 하나만 메서드 선언에 사용할 수 있습니다.

params 매개 변수의 선언된 형식은 컬렉션 형식이어야 합니다. 인식된 컬렉션 형식은 다음과 같습니다.

C# 13 이전에는 매개 변수가 단일 차원 배열이어야 합니다.

params 매개 변수를 사용하여 메서드를 호출하면 다음을 전달할 수 있습니다.

  • 배열 요소 형식의 쉼표로 구분된 인수 목록입니다.
  • 지정된 형식의 인수 컬렉션입니다.
  • 인수가 없습니다. 인수를 보내지 않는 경우 params 목록의 길이는 0입니다.

다음 예제에서는 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# 구문 및 사용법에 대 한 신뢰할 수 있는 소스 됩니다.