멤버 액세스 연산자 및 식, 점, 인덱서 및 호출 연산자입니다.
여러 연산자와 식을 사용하여 형식 멤버에 액세스합니다. 이러한 연산자에는 멤버 액세스(.
), 배열 요소 또는 인덱서 액세스([]
), index-from-end(^
), 범위(..
), null 조건부 연산자(?.
및 ?[]
), 메서드 호출(()
)이 포함됩니다. 여기에는 null 조건부 멤버 액세스(?.
) 및 인덱서 액세스(?[]
) 연산자가 포함됩니다.
.
(멤버 액세스): 네임스페이스 또는 형식의 멤버 액세스[]
(배열 요소 또는 인덱서 액세스): 배열 요소 또는 형식 인덱서 액세스?.
및?[]
(null 조건부 연산자): 피연산자가 null이 아닌 경우에만 멤버 또는 요소 액세스 작업 수행()
(호출): 액세스된 메서드나 대리자 호출^
(끝부터 인덱스): 요소 위치가 시퀀스의 끝에서 시작됨을 표시..
(범위): 시퀀스 요소의 범위를 가져오는 데 사용할 수 있는 인덱스 범위를 지정
멤버 액세스 식 .
다음 예제와 같이 .
토큰을 사용하여 네임스페이스 또는 형식의 멤버에 액세스합니다.
using
지시문의 다음 예제와 같이.
을 사용하여 네임스페이스 내에 중첩된 네임스페이스에 액세스합니다.
using System.Collections.Generic;
- 다음 코드와 같이
.
을 사용하여 ‘정규화된 이름’을 만들고 네임스페이스 내의 형식에 액세스합니다.
System.Collections.Generic.IEnumerable<int> numbers = [1, 2, 3];
using
지시문을 사용하여 정규화된 이름 사용을 선택 사항으로 설정합니다.
- 다음 코드와 같이
.
을 사용하여 정적 및 비정적 형식 멤버에 액세스합니다.
List<double> constants =
[
Math.PI,
Math.E
];
Console.WriteLine($"{constants.Count} values to show:");
Console.WriteLine(string.Join(", ", constants));
// Output:
// 2 values to show:
// 3.14159265358979, 2.71828182845905
.
을 사용하여 확장 메서드에 액세스할 수도 있습니다.
인덱서 연산자 []
대괄호 []
는 일반적으로 배열, 인덱서 또는 포인터 요소 액세스에 사용됩니다. C# 12부터 []
로 컬렉션 식을 묶습니다.
배열 액세스
다음 예제는 배열 요소에 액세스하는 방법을 보여 줍니다.
int[] fib = new int[10];
fib[0] = fib[1] = 1;
for (int i = 2; i < fib.Length; i++)
{
fib[i] = fib[i - 1] + fib[i - 2];
}
Console.WriteLine(fib[fib.Length - 1]); // output: 55
double[,] matrix = new double[2,2];
matrix[0,0] = 1.0;
matrix[0,1] = 2.0;
matrix[1,0] = matrix[1,1] = 3.0;
var determinant = matrix[0,0] * matrix[1,1] - matrix[1,0] * matrix[0,1];
Console.WriteLine(determinant); // output: -3
배열 인덱스가 배열의 해당 차원 범위를 벗어난 경우 IndexOutOfRangeException이 throw됩니다.
앞의 예제와 같이, 배열 형식을 선언하거나 배열 인스턴스를 인스턴스화할 때도 대괄호를 사용합니다.
배열에 대한 자세한 내용은 배열을 참조하세요.
인덱서 액세스
다음 예제는 .NET Dictionary<TKey,TValue> 형식을 사용하여 인덱서 액세스를 보여 줍니다.
var dict = new Dictionary<string, double>();
dict["one"] = 1;
dict["pi"] = Math.PI;
Console.WriteLine(dict["one"] + dict["pi"]); // output: 4.14159265358979
인덱서를 사용하면 배열 인덱싱과 비슷한 방법으로 사용자 정의 형식의 인스턴스를 인덱싱할 수 있습니다. 정수여야 하는 배열 인덱스와 달리, 인덱서 매개 변수는 임의 형식으로 선언할 수 있습니다.
인덱서에 대한 자세한 내용은 인덱서를 참조하세요.
다른 [] 용도
포인터 요소 액세스에 대한 자세한 내용은 포인터 관련 연산자 문서의 포인터 요소 액세스 연산자 섹션을 참조하세요. 컬렉션 식에 대한 내용은 컬렉션 식 문서를 참조하세요.
또한 대괄호를 사용하여 특성을 지정합니다.
[System.Diagnostics.Conditional("DEBUG")]
void TraceMethod() {}
Null 조건부 연산자 ?.
및 ?[]
null 조건부 연산자는 피연산자가 null이 아닌 것으로 평가되었을 때만 멤버 액세스, ?.
또는 요소 액세스, ?[]
, 연산을 피연산자에게 적용하며, 그렇지 않으면 null
을 반환합니다. 위의 명령이 반환하는 결과는 다음과 같습니다.
a
가null
로 평가되면a?.x
또는a?[x]
의 결과는null
입니다.a
가 null이 아닌 것으로 평가되면a?.x
또는a?[x]
의 결과는 각각a.x
또는a[x]
의 결과와 같습니다.참고 항목
a.x
또는a[x]
가 예외를 throw하면a?.x
또는a?[x]
는 null이 아닌a
와 동일한 예외를 throw합니다. 예를 들어a
가 null이 아닌 배열 인스턴스이고x
가a
의 경계 밖에 있는 경우,a?[x]
는 IndexOutOfRangeException을 throw합니다.
Null 조건부 연산자는 단락 연산자입니다. 즉 조건부 멤버나 요소 액세스 작업의 한 체인의 작업에서 null
을 반환하면 나머지 체인은 실행되지 않습니다. 다음 예제에서 A
가 null
로 평가되면 B
가 평가되지 않고, A
또는 B
가 null
로 평가되면 C
가 평가되지 않습니다.
A?.B?.Do(C);
A?.B?[C];
A
가 Null일 수 있지만 A가 Null이 아닌 경우 B
와 C
는 Null이 되지 않는다면 A
에만 Null 조건부 연산자를 적용하면 됩니다.
A?.B.C();
앞의 예제에서 B
은(는) 평가되지 않으며 A
이(가) null인 경우 C()
이(가) 호출되지 않습니다. 그러나 연결된 멤버 액세스가 중단될 경우(예: (A?.B).C()
처럼 괄호에 의해) 단락이 발생하지 않습니다.
다음 예제에서는 ?.
및 ?[]
연산자의 사용법을 보여 줍니다.
double SumNumbers(List<double[]> setsOfNumbers, int indexOfSetToSum)
{
return setsOfNumbers?[indexOfSetToSum]?.Sum() ?? double.NaN;
}
var sum1 = SumNumbers(null, 0);
Console.WriteLine(sum1); // output: NaN
List<double[]?> numberSets =
[
[1.0, 2.0, 3.0],
null
];
var sum2 = SumNumbers(numberSets, 0);
Console.WriteLine(sum2); // output: 6
var sum3 = SumNumbers(numberSets, 1);
Console.WriteLine(sum3); // output: NaN
namespace MemberAccessOperators2;
public static class NullConditionalShortCircuiting
{
public static void Main()
{
Person? person = null;
person?.Name.Write(); // no output: Write() is not called due to short-circuit.
try
{
(person?.Name).Write();
}
catch (NullReferenceException)
{
Console.WriteLine("NullReferenceException");
}; // output: NullReferenceException
}
}
public class Person
{
public required FullName Name { get; set; }
}
public class FullName
{
public required string FirstName { get; set; }
public required string LastName { get; set; }
public void Write() => Console.WriteLine($"{FirstName} {LastName}");
}
앞의 두 예제 중 첫 번째 예제에서는 null 병합 연산자 ??
를 사용하여 null 조건부 연산의 결과가 null
인 경우 평가할 대체 식을 지정합니다.
a.x
또는 a[x]
가 null을 허용하지 않는 값 형식인 경우 T
, a?.x
또는 a?[x]
는 해당하는 null 허용 값 형식 T?
입니다. T
형식의 식이 필요하면 다음 예제와 같이 null 병합 연산자 ??
를 null 조건식에 적용합니다.
int GetSumOfFirstTwoOrDefault(int[]? numbers)
{
if ((numbers?.Length ?? 0) < 2)
{
return 0;
}
return numbers[0] + numbers[1];
}
Console.WriteLine(GetSumOfFirstTwoOrDefault(null)); // output: 0
Console.WriteLine(GetSumOfFirstTwoOrDefault([])); // output: 0
Console.WriteLine(GetSumOfFirstTwoOrDefault([3, 4, 5])); // output: 7
앞의 예제에서 ??
연산자를 사용하지 않는 경우 numbers?.Length < 2
는 numbers
가 null
일 때 false
로 평가됩니다.
참고 항목
?.
연산자는 왼쪽 피연산자를 한 번만 계산하여 null이 아닌 것으로 확인된 후에는 null
로 변경할 수 없도록 보장합니다.
Null 조건부 멤버 액세스 연산자 ?.
를 Elvis 연산자라고도 합니다.
스레드로부터 안전한 대리자 호출
다음 코드에서처럼 ?.
연산자를 사용하여 대리자가 null이 아닌지 확인하고 스레드로부터 안전한 방식으로 호출합니다(예: 이벤트 발생 시).
PropertyChanged?.Invoke(…)
해당 코드는 다음 코드와 동일합니다.
var handler = this.PropertyChanged;
if (handler != null)
{
handler(…);
}
앞의 예제는 null이 아닌 handler
만 호출되도록 하는 스레드로부터 안전한 방법입니다. 대리자 인스턴스는 변경할 수 없으므로 스레드는 handler
지역 변수가 참조하는 개체를 변경할 수 없습니다. 특히 다른 스레드가 실행한 코드가 PropertyChanged
이벤트에서 구독을 취소하고 handler
를 호출하기 전에 PropertyChanged
가 null
이 되면 handler
에서 참조하는 개체는 영향을 받지 않습니다.
호출 식 ()
괄호(()
)를 사용하여 메서드 또는 대리자를 호출합니다.
다음 예제는 인수를 사용하거나 사용하지 않고 메서드를 호출하는 방법과 대리자를 호출하는 방법을 보여 줍니다.
Action<int> display = s => Console.WriteLine(s);
List<int> numbers =
[
10,
17
];
display(numbers.Count); // output: 2
numbers.Clear();
display(numbers.Count); // output: 0
new
연산자를 사용하여 생성자를 호출하는 경우에도 괄호를 사용합니다.
다른 () 용도
또한 괄호를 사용하여 식에서 연산을 계산하는 순서를 조정합니다. 자세한 내용은 C# 연산자를 참조하세요.
명시적 형식 변환을 수행하는 캐스트 식도 괄호를 사용합니다.
끝부터 인덱스 연산자 ^
인덱스와 범위 연산자는 셀 수 있는 형식과 함께 사용할 수 있습니다. 셀 수 있는 형식은 액세스 가능한 get
접근자가 있으며 이름이 Count
또는 Length
인 int
속성이 있는 형식입니다. 컬렉션 식은 셀 수 있는 형식에도 의존합니다.
^
연산자는 시퀀스의 끝에서 요소 위치를 나타냅니다. 시퀀스 길이 length
의 경우 ^n
은 시퀀스의 시작에서 오프셋 length - n
인 요소를 가리킵니다. 예를 들어 ^1
은 시퀀스의 마지막 요소를 가리키고, ^length
는 시퀀스의 첫 번째 요소를 가리킵니다.
int[] xs = [0, 10, 20, 30, 40];
int last = xs[^1];
Console.WriteLine(last); // output: 40
List<string> lines = ["one", "two", "three", "four"];
string prelast = lines[^2];
Console.WriteLine(prelast); // output: three
string word = "Twenty";
Index toFirst = ^word.Length;
char first = word[toFirst];
Console.WriteLine(first); // output: T
위 예제에서와 같이 식 ^e
는 System.Index 형식입니다. 식 ^e
에서 e
의 결과는 암시적으로 int
으로 변환할 수 있어야 합니다.
^
연산자를 범위 연산자와 함께 사용하여 인덱스 범위를 만들 수도 있습니다. 자세한 내용은 인덱스와 범위를 참조하세요.
C# 13부터 End 연산자의 인덱스를 개체 이니셜라이저에 사용할 수 있습니다.
범위 연산자 ..
..
연산자는 인덱스 범위의 시작과 끝을 피연산자로 지정합니다. 왼쪽 피연산자는 범위의 시작(포함)입니다. 오른쪽 피연산자는 범위의 끝(제외)입니다. 다음 예제에서와 같이 피연산자 중 하나는 시퀀스의 시작부터 또는 끝부터 인덱스가 될 수 있습니다.
int[] numbers = [0, 10, 20, 30, 40, 50];
int start = 1;
int amountToTake = 3;
int[] subset = numbers[start..(start + amountToTake)];
Display(subset); // output: 10 20 30
int margin = 1;
int[] inner = numbers[margin..^margin];
Display(inner); // output: 10 20 30 40
string line = "one two three";
int amountToTakeFromEnd = 5;
Range endIndices = ^amountToTakeFromEnd..^0;
string end = line[endIndices];
Console.WriteLine(end); // output: three
void Display<T>(IEnumerable<T> xs) => Console.WriteLine(string.Join(" ", xs));
위 예제에서와 같이 식 a..b
는 System.Range 형식입니다. 식 a..b
에서 a
및 b
의 결과는 암시적으로 Int32 또는 Index로 변환할 수 있어야 합니다.
Important
int
에서 Index
로의 암시적 변환은 값이 음수일 경우 ArgumentOutOfRangeException을 throw합니다.
..
연산자의 피연산자 중 하나를 생략하여 개방형 범위를 지정할 수 있습니다.
a..
는a..^0
와 같습니다...b
는0..b
와 같습니다...
는0..^0
와 같습니다.
int[] numbers = [0, 10, 20, 30, 40, 50];
int amountToDrop = numbers.Length / 2;
int[] rightHalf = numbers[amountToDrop..];
Display(rightHalf); // output: 30 40 50
int[] leftHalf = numbers[..^amountToDrop];
Display(leftHalf); // output: 0 10 20
int[] all = numbers[..];
Display(all); // output: 0 10 20 30 40 50
void Display<T>(IEnumerable<T> xs) => Console.WriteLine(string.Join(" ", xs));
다음 표에서는 컬렉션 범위를 표현하는 다양한 방법을 보여 줍니다.
범위 연산자 식 | 설명 |
---|---|
.. |
컬렉션의 모든 값입니다. |
..end |
처음부터 end 까지의 값입니다. |
start.. |
start 부터 끝까지의 값입니다. |
start..end |
start 부터 end 까지의 값입니다. |
^start.. |
start 부터 끝까지의 값(끝에서 계산)입니다. |
..^end |
시작부터 end 까지의 값(끝부터 계산)입니다. |
start..^end |
start 부터 end 까지의 값(끝부터 계산)입니다. |
^start..^end |
start 부터 end 까지의 값(둘 다 끝부터 계산)입니다. |
다음 예제에서는 앞의 표에 표시된 모든 범위를 사용하는 효과를 보여 줍니다.
int[] oneThroughTen =
[
1, 2, 3, 4, 5, 6, 7, 8, 9, 10
];
Write(oneThroughTen, ..);
Write(oneThroughTen, ..3);
Write(oneThroughTen, 2..);
Write(oneThroughTen, 3..5);
Write(oneThroughTen, ^2..);
Write(oneThroughTen, ..^3);
Write(oneThroughTen, 3..^4);
Write(oneThroughTen, ^4..^2);
static void Write(int[] values, Range range) =>
Console.WriteLine($"{range}:\t{string.Join(", ", values[range])}");
// Sample output:
// 0..^0: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
// 0..3: 1, 2, 3
// 2..^0: 3, 4, 5, 6, 7, 8, 9, 10
// 3..5: 4, 5
// ^2..^0: 9, 10
// 0..^3: 1, 2, 3, 4, 5, 6, 7
// 3..^4: 4, 5, 6
// ^4..^2: 7, 8
자세한 내용은 인덱스와 범위를 참조하세요.
..
토큰은 컬렉션 식의 spread 요소에도 사용됩니다.
연산자 오버로드 가능성
.
, ()
, ^
및 ..
연산자는 오버로드할 수 없습니다. []
연산자도 오버로드할 수 없는 연산자로 간주됩니다. 인덱서를 사용하여 사용자 정의 형식의 인덱싱을 지원합니다.
C# 언어 사양
자세한 내용은 C# 언어 사양의 다음 섹션을 참조하세요.
인덱스 및 범위에 대한 자세한 내용은 기능 제안 노트를 참조하세요.
참고 항목
.NET