선언문
선언 문은 새 변수를 선언하고 필요에 따라 초기화합니다. 모든 변수에 선언된 형식이 있습니다. .NET 형식 시스템에 관한 문서에서 형식에 대해 자세히 알아볼 수 있습니다. 일반적으로 선언에는 형식과 변수 이름이 포함됩니다. 또한 =
연산자 뒤에 식이 있는 초기화를 포함할 수도 있습니다. 형식을 var
로 바꿀 수 있습니다. 선언 또는 식에는 새 변수가 기존 스토리지 위치를 참조한다고 선언하는 ref
한정자가 포함될 수 있습니다.
암시적 형식 지역 변수
메서드 범위에서 선언된 변수에 암시적 “형식” var
을 사용할 수 있습니다. 암시적 형식 지역 변수는 형식을 직접 선언한 것처럼 강력한 형식이지만 컴파일러가 형식을 결정합니다. a
및 b
의 다음 두 선언은 기능이 동일합니다.
var a = 10; // Implicitly typed.
int b = 10; // Explicitly typed.
중요
null 허용 참조 형식을 사용하도록 설정하고 var
를 사용하면 식 형식이 null 허용이 아니더라도 항상 null 허용 참조 형식을 의미합니다. 컴파일러의 null 상태 분석은 잠재적 null
값이 역참조되지 않도록 보호합니다. null일 수 있는 식에 해당 변수가 할당되지 않은 경우 컴파일러는 경고를 내보내지 않습니다. null일 수 있는 식에 변수를 할당하는 경우 경고를 방지하기 위해 이 변수를 역참조하기 전에 null이 아닌지 테스트해야 합니다.
var
키워드는 일반적으로 생성자 호출 식과 함께 사용됩니다. var
를 사용하면 다음 예제와 같이 변수 선언 및 개체 인스턴스화에서 형식 이름을 반복하지 않을 수 있습니다.
var xs = new List<int>();
C# 9.0부터 대상으로 형식화된 new
식을 대신 사용할 수 있습니다.
List<int> xs = new();
List<int>? ys = new();
패턴 일치에서 var
키워드는 var
패턴에 사용됩니다.
다음 예제에서는 두 가지 쿼리 식을 보여 줍니다. 첫 번째 식에서는 var
을 사용할 수 있지만, 쿼리 결과의 형식을 IEnumerable<string>
으로 명시적으로 정의할 수 있기 때문에 필요하지 않습니다. 그러나 두 번째 식에서 var
은 결과가 익명 형식의 컬렉션이 되도록 허용하고 해당 형식의 이름은 컴파일러 자체에만 액세스할 수 있습니다. var
을 사용하면 결과에 대한 새 클래스를 만들 필요가 없습니다. 예제 #2에서는 foreach
반복 변수 item
도 암시적 형식이어야 합니다.
// Example #1: var is optional when
// the select clause specifies a string
string[] words = { "apple", "strawberry", "grape", "peach", "banana" };
var wordQuery = from word in words
where word[0] == 'g'
select word;
// Because each element in the sequence is a string,
// not an anonymous type, var is optional here also.
foreach (string s in wordQuery)
{
Console.WriteLine(s);
}
// Example #2: var is required because
// the select clause specifies an anonymous type
var custQuery = from cust in customers
where cust.City == "Phoenix"
select new { cust.Name, cust.Phone };
// var must be used because each item
// in the sequence is an anonymous type
foreach (var item in custQuery)
{
Console.WriteLine("Name={0}, Phone={1}", item.Name, item.Phone);
}
참조 로컬
변수 형식 앞에 ref
키워드를 추가하여 ref
지역 변수를 선언합니다. ref
로컬은 다른 스토리지를 참조하는 변수입니다. GetContactInformation
메서드가 다음과 같이 참조 반환으로 선언된다고 가정합니다.
public ref Person GetContactInformation(string fname, string lname)
다음 두 할당을 대조해 보겠습니다.
Person p = contacts.GetContactInformation("Brandie", "Best");
ref Person p2 = ref contacts.GetContactInformation("Brandie", "Best");
변수 p
는 의 반환 값 GetContactInformation
복사본을 보유합니다. 의 반환GetContactInformation
과 ref
는 별도의 스토리지 위치입니다. 의 p
속성을 변경하면 의 Person
복사본이 변경됩니다.
변수 p2
는 의 GetContactInformation
반환에 대한 ref
스토리지 위치를 나타냅니다. 의 반환GetContactInformation
과 동일한 스토리지입니다ref
. 의 속성을 변경하는 경우 의 p2
단일 인스턴스를 변경합니다 Person
.
동일한 방법으로 참조로 값에 액세스할 수 있습니다. 경우에 따라 참조로 값에 액세스하면 비용이 많이 들 수 있는 복사 작업을 피함으로써 성능이 향상됩니다. 예를 들어, 다음 명령문은 값을 참조하는 데 사용되는 참조 로컬 값을 정의하는 방법을 보여줍니다.
ref VeryLargeStruct reflocal = ref veryLargeStruct;
ref
키워드는 지역 변수 선언 앞 ‘그리고’ 두 번째 예의 값 앞에 사용됩니다. 두 예에서 변수 선언과 할당에 ref
키워드를 둘 다 포함하지 않으면 컴파일러 오류 CS8172, "값을 사용하여 참조 형식 변수를 초기화할 수 없습니다."가 생성됩니다.
ref VeryLargeStruct reflocal = ref veryLargeStruct; // initialization
refLocal = ref anotherVeryLargeStruct; // reassigned, refLocal refers to different storage.
참조 지역 변수는 선언될 때 초기화되어야 합니다.
다음 예제에서는 정수 값의 배열을 저장하는 NumberStore
클래스를 정의합니다. FindNumber
메서드는 인수로 전달된 숫자보다 크거나 같은 첫 번째 숫자를 참조로 반환합니다. 인수보다 크거나 같은 숫자가 없으면 메서드는 인덱스 0의 숫자를 반환합니다.
using System;
class NumberStore
{
int[] numbers = { 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023 };
public ref int FindNumber(int target)
{
for (int ctr = 0; ctr < numbers.Length; ctr++)
{
if (numbers[ctr] >= target)
return ref numbers[ctr];
}
return ref numbers[0];
}
public override string ToString() => string.Join(" ", numbers);
}
다음 예제에서는 NumberStore.FindNumber
메서드를 호출하여 16보다 크거나 같은 첫 번째 값을 검색합니다. 그런 다음 호출자가 메서드에서 반환된 값을 두 배로 만듭니다. 예제의 출력에서는 NumberStore
인스턴스의 배열 요소 값에 반영된 변경 내용을 보여줍니다.
var store = new NumberStore();
Console.WriteLine($"Original sequence: {store.ToString()}");
int number = 16;
ref var value = ref store.FindNumber(number);
value *= 2;
Console.WriteLine($"New sequence: {store.ToString()}");
// The example displays the following output:
// Original sequence: 1 3 7 15 31 63 127 255 511 1023
// New sequence: 1 3 7 15 62 63 127 255 511 1023
참조 반환 값이 지원되지 않을 경우 이러한 작업은 해당 값과 함께 배열 요소의 인덱스를 반환하여 수행됩니다. 그런 다음 호출자는 이 인덱스를 사용하여 별도 메서드 호출에서 값을 수정할 수 있습니다. 그러나 호출자가 인덱스를 수정하여 다른 배열 값에 액세스하고 수정할 수도 있습니다.
다음 예제에서는 FindNumber
메서드를 다시 작성하여 참조 지역 변수 재할당을 사용하는 방법을 보여줍니다.
using System;
class NumberStore
{
int[] numbers = { 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023 };
public ref int FindNumber(int target)
{
ref int returnVal = ref numbers[0];
var ctr = numbers.Length - 1;
while ((ctr >= 0) && (numbers[ctr] >= target))
{
returnVal = ref numbers[ctr];
ctr--;
}
return ref returnVal;
}
public override string ToString() => string.Join(" ", numbers);
}
이 두 번째 버전은 배열이 끝에서부터 처음을 향해 반복되어 검사할 항목 수가 줄어들기 때문에 찾은 숫자가 배열의 끝에 더 가까운 시나리오에서 더 긴 시퀀스를 사용하여 더 효율적입니다.
컴파일러는 ref
변수(ref struct
형식의 ref
지역 변수, ref
매개 변수 및 ref
필드)에 범위 규칙을 적용합니다. 규칙은 참조가 참조하는 개체보다 오래 지속되지 않도록 합니다. 메서드 매개 변수 문서에서 범위 지정 규칙에 대한 섹션을 참조하세요.
ref 및 readonly
readonly
한정자는 ref
지역 변수 및 ref
필드에 적용할 수 있습니다. readonly
한정자는 식의 오른쪽에 영향을 줍니다. 다음 예제 선언을 참조하세요.
ref readonly int aConstant; // aConstant can't be value-reassigned.
readonly ref int Storage; // Storage can't be ref-reassigned.
readonly ref readonly int CantChange; // CantChange can't be value-reassigned or ref-reassigned.
- 값 재할당은 변수 값이 다시 할당됨을 의미합니다.
- 참조 할당은 변수가 이제 다른 개체를 참조한다는 것을 의미합니다.
readonly ref
및 readonly ref readonly
선언은 ref struct
의 ref
필드에만 유효합니다.
scoped ref
상황별 키워드 scoped
는 값의 수명을 제한합니다. scoped
한정자는 ref-safe-to-escape 또는 safe-to-escape 수명을 각각 현재 메서드로 제한합니다. 실질적으로 scoped
한정자 추가는 코드가 변수의 수명을 연장하지 않는다는 어설션입니다.
scoped
는 매개 변수 또는 지역 변수에 적용할 수 있습니다. 형식이 ref struct
인 경우 매개 변수 및 지역 변수에 scoped
한정자를 적용할 수 있습니다. 그렇지 않으면 참조 형식인 지역 변수에만 scoped
한정자를 적용할 수 있습니다. 여기에는 ref
한정자를 사용하여 선언된 지역 변수와 in
, ref
또는 out
한정자를 사용하여 선언된 매개 변수가 포함됩니다.
scoped
한정자는 형식이 ref struct
인 경우 struct
에 선언된 메서드의 this
, out
매개 변수 및 ref
매개 변수에 암시적으로 추가됩니다.