Notice
Recent Posts
Recent Comments
Link
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
Archives
Today
Total
관리 메뉴

nomad-programmer

[Programming/C#] ?? 연산자와 ??= 연산자 본문

Programming/C#

[Programming/C#] ?? 연산자와 ??= 연산자

scii 2020. 10. 17. 00:08

??= 연산자는 C# 8.0 버전에서 추가된 아주 유용한 연산자 중 하나이다. ??= 연산자는 "null 병합 할당 연산자" 라고 부르며 ??= 연산자는 왼쪽 피연산자가 null로 평가되는 경우에만 오른쪽 피연산자의 값을 왼쪽 피연산제 대입한다. 왼쪽 피연산자가 null이 아닌것으로 평가되면 ??= 연산자는 오른쪽 피연산자를 평가하지 않는다.

?? 연산자는 "null 병합 연산자" 라고 부르며 null이 아닌 경우 왼쪽 피연산자의 값을 반환한다. 그렇지 않으면 오른쪽 피연산자를 평가하고 그 결과를 반환한다. ?? 연산자는 왼쪽 피연산자가 null 아닌 것으로 평가되면 ?? 연산자는 오픈쪽 피연산자를 평가하지 않는다.

if (variable is null)
{
    variable = expression;
}

위의 코드는 다음과 같이 표현할 수 있다.

variable ??= expression;

다음의 코드는 ??= (null 병합 할당 연산자) 를 사용하지 않았을 때의 코드이다.

using System;
using System.Collections.Generic;

namespace test
{
    class Program
    {
        static void Main(string[] args)
        {
            int? foo = null;
            List<int> numbers = null;

            if (numbers is null)
            {
                (numbers = new List<int>()).Add(5);
            }
            Console.WriteLine($"numbers: {string.Join(' ', numbers)}");

            if(foo is null)
            {
                foo = 10;
                numbers?.Add((int)foo);
            }
            else
            {
                numbers?.Add((int)foo);
            }
            Console.WriteLine($"numbers: {string.Join(' ', numbers)}");

            Console.WriteLine($"foo: {foo}");
        }
    }
}

/* 결과

numbers: 5
numbers: 5 10
foo: 10

*/

다음의 코드는 ??= (null 병합 할당 연산자) 를 사용하였을 때의 코드다.

using System;
using System.Collections.Generic;

namespace test
{
    class Program
    {
        static void Main(string[] args)
        {
            int? foo = null;
            List<int> numbers = null;

            // numbers가 null이라면 새로운 리스트를 생성후 numbers에 대입한다. 그 후 5를 넣는다.
            (numbers ??= new List<int>()).Add(5);
            Console.WriteLine($"numbers: {string.Join(' ', numbers)}");

            // foo가 null이라면 foo 변수에다가 10을 대입하고 foo의 값을 numbers 리스트에 추가한다.
            // 그렇지 않고 foo가 null이 아니라 값이 존재한다면 foo의 값을 numbers 리스트에 추가한다.
            numbers?.Add(foo ??= 10);
            Console.WriteLine($"numbers: {string.Join(' ', numbers)}");

            Console.WriteLine($"foo: {foo}");
        }
    }
}

/* 결과

numbers: 5
numbers: 5 10
foo: 10

*/

null 병합 할당 연산자를 사용하니 코드가 상당히 깔끔해진것을 볼 수 있다. C# 8.0 버전부터 ?? 및 ??= 연산자의 왼쪽 피연산자 형식은 null을 허용하지 않는 값 형식일 수 없다.

??= 연산자의 왼쪽 피연산자는 변수, 속성 또는 인덱서 요소여야 한다.

결합 순서

a ?? b ?? c
d ??= e ??= f

위의 코드는 다음과 같은 결합 순서를 가진다.

a ?? (b ?? c)
d ??= (e ??= f)

?? 및 ??= 연산자의 유용한 사용 예

?? 및 ??= 연산자는 다음과 같은 상황에서 유용하게 사용할 수 있다. 

1. null 병합 연산자 ?. 및 ?[] 가 있는 식에서 ?? 연산자를 사용하여 null 조건부 연산을 사용한 식의 결과가 null인 경우 평가하는 대체 식을 제공할 수 있다.

using System;
using System.Collections.Generic;

namespace test
{
    class Program
    {
        public static double SumNumbers(List<double[]> setOfNumbers, int indexOfSetToSum)
        {
            // setOfNumbers가 null이 아니라면 indexOfSetToSum 숫자로 색인을 진행한다.
            // 색인 결과가 null이 아닌 경우 Sum 메소드를 호출하여 List의 값을 모두 더한다.
            // 그렇지않고 색인 결과가 null 이라면, NaN 을 반환한다.
            return setOfNumbers?[indexOfSetToSum]?.Sum() ?? double.NaN;
        }

        static void Main(string[] args)
        {
            double sum = SumNumbers(null, 0);
            Console.WriteLine($"sum: {sum}");
        }
    }
}

/* 결과

sum: NaN

*/

2. nullable 값 형식을 사용하고 기본값 유형의 값을 제공해야 할 때 ?? 연산자를 사용하여 nullable 값이 null 인 경우 제공할 값을 지정한다.

int? a = null;
int b = a ?? -1;
Console.WriteLine(b);

/* 결과

-1

*/

3. C# 7.0 부터 ?? 연산자의 오른쪽 피연산자로 throw 식을 사용하여 인수 확인 코드를 보다 간결하게 만들 수 있다.

public string Name
{
    get => name;
    set => name = value ?? throw new ArgumentNullException(nameof(value), "Name cannot be null");
}
Comments