반응형

n Nulnud-coalescing operator  

   NULL 병합 연산자

   ??  , ??=

 

특징  : 오른쪽 결합

 

 

null 병합 연산자 라고 부르는 ?? 연산자는

null이 아닌 경우 왼쪽 값을 반환하고 null인 경우 오른쪽 값을 확인 하고 반환하는 연산자다.

만약 왼쪽값이 null 이 아닌경우  오른쪽 값을 확인하지 않는다.

 

 

            int? a = 17;
            int b = 15;
            int? c = null;
            
            Console.WriteLine(a ?? b); // Output :17,  왼쪽값이 null이 아니기때문에 17.
            Console.WriteLine(c ?? b); //Output :15,   왼쪽(null)->오른쪽 값을 확인해 본뒤 b반환
            Console.WriteLine(c ?? c); //Output : ,    둘다 null 이기 때문에 null
            //Console.WriteLine(b ?? c); Err ?? 연산자는 int 및 int? 형식의 피연산자에 적용할수 없다

int? 가 뭔지 모르겠다면 이 블로그의 Nullable 을 보고오는게 좋다.

https://programing-note.tistory.com/entry/c-Nullabe-%EA%B0%92-%ED%98%95%EC%8B%9D

 

 

c#8.0 이상에서 사용할수 있는null 병합 할당 연산자(??=)는  null 병합 연산자 와 비슷하다. 

  왼쪽 값이 null인경우 우측 값을 할당시켜준다. 물론 좌측값이 null이 아닌경우 우측값을 확인하지 않는다.

            int? a = null;
            int b = 15;
            a ??= b; 좌측값이 null 이기때문에 우측b 값을 확인후 a값에 대입한다.
            Console.WriteLine(a); // 15

 이 연산자는 가끔 유용하게 쓰일수있다.

 

 

예시로는 null인지 확인하는 구문을 간단하게 만들수있다.

            if (isNull is null)
            {
                isNull = 10;
            }

이러한 연산을

            isNull ??= 10;

이렇게 간단하게 쓸수 있다.

 

 

 

Null 조건부 연산자 ?. 및 ?[]

C# 6이상부터 사용할수 있는 연산자다.

이는 Null이 아닌것으로 평가되었을 때만 멤버 엑세스 ?. ,또는 요소 엑세스?[], 연산을 피연산자 에게 적용하고

 Null 인것으로 평가되었을경우 Null을 반환한다.

 

  조건부 멤버나 요소 액세스 작업에서 null 을 반환하면 나머지 체인은 실행되지 않는다.

 

A?.B?.c

위 와 같을때 A가 Null일때 B는 평가되지 않는다.

이와 같이 A또는 B가 Null일경우 C는 평가되지 않는다.

 

아래 윈도우 예제 코드를 보면 쉽게 이해할수 있다.

            
            double SumNumbers(List<double[]> setsOfNumbers, int indexOfSetToSum)
            {
                return setsOfNumbers?[indexOfSetToSum]?.Sum() ?? double.NaN;
            }

            var sum1 = SumNumbers(null, 0);
            Console.WriteLine(sum1);  // output: NaN
            var numberSets = new List<double[]>
                 {
                     new[] { 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
            
                //var sum4 = SumNumbers(numberSets, 2); 예외발생

위 코드처럼 코드를 짤때 Null 이 들어가 오류가 생기는 상황을 간단하게 처리할수있다.

 

 

주의 할점

a.x 또는 a[x] 가 예외를 던지면 a?.x 또는 a?[x] 는 null이 아닌 예외를 던진다.

위의 예제 마지막줄 처럼 x가 a의 배열의 크기 보다 큰경우 a?[x] 는 IndexOutOFRangeExcption 예외를 던진다.

 

참고

?. 연산자는 왼쪽 연산자를 한번만 계산하고 null이 아닌것을 확인한 후에는 null로 변경할수 없도록 보장한다.

 

또한 스레드를 사용할때도 안전하게 호출할수 있다.

스레드를 호출할때 null인지 확인하고 호출하는 것을

var handler = this.PropertyChanged;
if (handler != null)
{
    handler(…);
}
 
 를 아래처럼 변경할수있다

PropertyChanged?.Invoke(…)

위 상황처럼 간단하게  사용할수있다

 

참고 링크 :docs.microsoft.com;

'언어 > C#' 카테고리의 다른 글

c# Nullable값 형식  (0) 2022.03.24
c# Tuple  (0) 2022.03.23
C# Ref,Out 키워드 와 in 키워드  (0) 2022.03.16
반응형

Nullable 값 형식 : 기본 값 형식의 모든 값과 추가 null 값을 나타낼수 있다.

 

예시로 

 bool 변수는 true false 두가지를 나타내는게 가능하고 Null은 표현불가능하다.

하지만 null 이 필요할수 있기때문에  자료형 뒤에 ? 를 붙이면 null 표현이 가능해진다.

 

사용 방식 

값 타입 자료형 ? 변수명

    int? a = 10;
    int? b = null;
   // int c = null;  null을 허용하지 않는 값 형식
   
   class A{}
  // A a?; null 을 허용하지 않는  값 형식 이여야 한다.
  
  int?[] arr = new int?[10];
  //배열 선언방법

 

 

Nullable 값 형식에서 기본 형식으로 변환 

   int? a = 10;
   int b = 0;
   b = a ?? -1;
   // a가 null 이면 -1이 들어가고  null이 아닐경우 a값이 그대로 들어가 10이 들어간다.

 

?? 연산자는 왼쪽 값이 null 이면  오른쪽 값을 사용하고 null이 아닐경우 왼쪽 값을 사용하는 

일종의 null 을 검사하는 연산자다. 

 

 

 

 

 

is

is 연산자로는 Nullable 형식인지 아닌지 구분 불가하다

마소 예제코드

int? a = 1;
int  b = 2;

if ( a is int )
{
  Console.WriteLine(int? instance is compatible with int);
}
if (b is int?)
{
    Console.WriteLine("int instance is compatible with int?");
}

// Output:
// int? instance is compatible with int
// int instance is compatible with int?

 

 

 

GetType 

Object.GetType은 Null 허용값 형식의 boxing으로 기본형식 값의 boxing과 동일하게 나온다.

GetType은 기본형식 Type 인스턴스를 반환한다.

int? a = 17;
Type typeOfA = a.GetType();
Console.WriteLine(typeOfA.FullName);
// Output:
// System.Int32

 

그럼으로 아래 코드 처럼 구분할수 있다

마소코드

Console.WriteLine($"int? is {(IsNullable(typeof(int?)) ? "nullable" : "non nullable")} value type");
Console.WriteLine($"int is {(IsNullable(typeof(int)) ? "nullable" : "non-nullable")} value type");

bool IsNullable(Type type) => Nullable.GetUnderlyingType(type) != null;

// Output:
// int? is nullable value type
// int is non-nullable value type

 

 

 

참고 링크 :docs.microsoft.com;

'언어 > C#' 카테고리의 다른 글

c# ?? 연산자 (Null 병합 연산자)  (0) 2022.07.12
c# Tuple  (0) 2022.03.23
C# Ref,Out 키워드 와 in 키워드  (0) 2022.03.16
반응형

c# 튜플 : 간단한 데이터 구조로 여러 데이터 요소를 그룹화 할수있다.

 

튜플 변수 선언

(원하는 자료형 , 원하는 자료형 , ... ) 변수명;

// 선언과 동시에 초기화
(double,int) t = ( 1.1 , 3 );
 t.Item1 = 10;

(string name , int age) t2;
t2 = (name: "Mok", age: 5);
t2.age = 20;

이런식으로 간단하게 그룹화 할수있다

또한 튜플은 함수의 반환형식 에서도 사용할수 있다.

 

 

튜플 함수 사용

(원하는 자료형 , 원하는 자료형 , ... ) 함수명 (매개변수s..) { 내부구현...  retrun(반환값,...) };

  
   (int min, int max) FindMinMax(int[] input)
        {
         //... 내부구현
            return (min, max);
        }

위 함수처럼 자료형 클래스 말고도 묶어서 사용이 가능하다.

 

 

c# 에서 제공한 튜플의 사용 사례를 보면 더욱 쉽게 알수있다.

       //마소 c# 튜플 사용 사례
       var ys = new[] { -9, 0, 67, 100 };
        var (minimum, maximum) = FindMinMax(ys);
        // Output:
        // Limits of [-9 0 67 100] are -9 and 100

        (int min, int max) FindMinMax(int[] input)
        {
            if (input is null || input.Length == 0)
            {
            }

            var min = int.MaxValue;
            var max = int.MinValue;
            foreach (var i in input)
            {
                if (i < min)
                {
                    min = i;
                }
                if (i > max)
                {
                    max = i;
                }
            }
            return (min, max);
        }

위 사용 사례에서 보이는것 처럼 minimum 과 maximum 을 따로 구하지 않고 한번의 묶음으로 처리하여

하나의 함수로 처리하는것을 볼수있다.

 

 

또다른 사용 예시로는 비교문에서 알수있다.

//튜플 비교문


(int a, byte b) left = (5, 10);
(long a, int b) right = (5, 10);
Console.WriteLine(left == right);  // output: True
Console.WriteLine(left != right);  // output: False

var t1 = (A: 5, B: 10);
var t2 = (B: 5, A: 10);
Console.WriteLine(t1 == t2);  // output: True
Console.WriteLine(t1 != t2);  // output: False

보이는것 처럼 두개의 묶음으로 비교하여 비교문을 줄일수 있다.

 

 

튜플은  Dictionary 에서도 사용이 가능하다.

var limitsLookup = new Dictionary<int, (int Min, int Max)>()
{
    [2] = (4, 10),
    [4] = (10, 20),
    [6] = (0, 23)
};

if (limitsLookup.TryGetValue(4, out (int Min, int Max) limits))
{
    Console.WriteLine($"Found limits: min is {limits.Min}, max is {limits.Max}");
}
// Output:
// Found limits: min is 10, max is 20

이처럼 쉽게 저장하고 사용하는것을 볼수있다.

 

 

참고

 1. 지금 사용한 튜플 형식사용은 기존 Tuple class 와는 다르다.

    ㄴ 지금 사용한 형식은 System.ValueTuple 이고 Tuple class는 System.Tuple 이다.

 2.  System.ValueTuple 형식은 데이터 멤버는 필드이다.

 2.1  System.Tuple 형식은 데이터 멤버는 속성이다.

 

3. anonymous type 과 비교도 가능하다.

var apple = new { Item = "apples", Price = 1.35 };
(string,double) appe = ( "apples", 1.35 );

if(appe == appe)
{
Console.WriteLine("true"); //true
}

 

 

익명 타입, 튜플, vlaueTuple 들의 주요 차이점은 다음과 같다

이름 액세스 한정자 형식 사용자 지정 멤버 이름 분해 지원 식트리 지원
anonymous type internal o x o
Tuple public x x o
ValueTuple public o o x

 

참고자료 : docs.microsoft.com

'언어 > C#' 카테고리의 다른 글

c# ?? 연산자 (Null 병합 연산자)  (0) 2022.07.12
c# Nullable값 형식  (0) 2022.03.24
C# Ref,Out 키워드 와 in 키워드  (0) 2022.03.16
반응형

 

Ref 키워드와 Out 키워드를 더 잘 이해하기 위해서는 call by refurence , call by value를 알고 보는 게 좋다.

Ref , Out 키워드

정의부터 설명하자면

 Ref Pass by Reference

   ㄴ 얕은 복사 매개변수 지정자

Out Output Parameters

   ㄴ 출력용 매개변수 - 내부에서 값을 할당해주어야 한다.

 

 

사용 방법

 ref out 사용법

 

함수 선언 시

 리턴 값 함수명 (ref & out키워드 매개변수 타입  변수 이름 ) <-함수를 선언 또는 구현 시에도 매개변수 앞에 써줘야 한다.

함수 사용 시

 함수명( ref & out 키워드 변수 ) <- 사용할 때에도 매개변수 앞에다 ref 키워드를 써줘야 한다.

    public void FuncRefB(ref int A) 
    {
        A++;
    }

    public void Func( int A)
    {
        A++;
    }

    public void FuncOutB(out int A)
    {
        A = 10; // 내부에서 값을 대입 하지 않으면 오류가 나온다.
    }
   
       ...
       {
        int A;
        
        FuncOutB(out A);
          Console.WriteLine(A); // 내부에서 생성해서 받는다 Call by Refurence + @  10
        Func(A);
          Console.WriteLine(A); // 늘어나지 않는다 Call by Value   10
        FuncRefB(ref A);
          Console.WriteLine(A); // 값이 늘어난다. Call by Refurence  11
        }

이처럼 c나 c++처럼 포인터 참조형처럼 Call by Reference 형식으로 넘겨줄 수 있다.

 

(참고할 점)

1. Ref 키워드는 할당이 되어있어야 한다.

2. Out 은 Ref처럼 할당이 되어있지 않아도 사용 가능하지만. 내부에서 새로 할당한다.(이전 값은 사라짐)

3. Ref int -> int 오버로드는 가능하나 Out -> Ref , in 오버로드는 불가능하다.

 

 

구조체 같은 경우 기본적으로 call by value 형식이기 때문에 구조체를 ref 키워드로 넘겨주면 
얕은 복사(주소 값)처럼 사용할 수 있다.

 

in 키워드

in : 읽기 전용 매개변수 지정자

 

함수 선언 시

 리턴 값 함수명 ( in 키워드 매개변수 타입  변수이름 ) <-함수를 선언 또는 구현 시에도 매개변수 앞에 써줘야 한다.

함수 사용 시

 함수명(  키워드 변수 ) <-  in은 사용할 때 앞에 붙여 주지 않는다.

 

void FuncIn(in int num)
{
  //num++; 내부 수정불가능
  
}

내부에서 수정이 불가능하기 때문에 혹여나 다른 값을 입력하거나 수정하는 실수를 미연에 방지할 수 있다.

 

 

Ref 와 in 키워드는  복사 비용을 참조로 변경하여 성능 향상을 생각할수 있다.

 

 

--추가--

 

out 키워드 의경우 C#7.0 버전부터는 튜블 반환을 지원했다.

튜플 반환이 있기전에 여러값을 반환 할수 없었기 때문에 out을 주로 사용하였다.

 

(int sum, int product) Calculate(int a, int b) => (a + b, a * b);

 

 

void Calculate(int a, int b, out int sum, out int product)
{
    sum = a + b;
    product = a * b;
}

Calculate(2, 3, out int sum, out int product);

 

out 키워드는 성능이 중요한 코드나 오래된 api, 호환성 때문에 쓰인다.

 

 

 

반복적으로 많이 쓰일때 생성되는것보다 out으로 불필요한 메모리할당과 GC부하를 줄일수있다.

 

 

 

 

 

 

 

 

 

 

 

 

 

'언어 > C#' 카테고리의 다른 글

c# ?? 연산자 (Null 병합 연산자)  (0) 2022.07.12
c# Nullable값 형식  (0) 2022.03.24
c# Tuple  (0) 2022.03.23

+ Recent posts