C #에서 두 개의 물음표가 함께 무엇을 의미합니까?


1629

이 코드 줄을 가로 질러 달려보십시오.

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

두 개의 물음표는 어떤 의미입니까, 어떤 종류의 삼항 연산자입니까? Google에서 찾기가 어렵습니다.


50
그것은 확실히입니다 하지 삼항 연산자 - 그것은 단지 두 개의 피연산자가 있습니다! 그것은 (조건 연산자 같은 비트의 삼원)하지만 널 유착 연산자 이진 연산자이다.
Jon Skeet

90
나는 한동안 자바를 전문적으로 사용하고 있었기 때문에 예비 고용주가 이전에 C # 능력에 대해 의심을 표명 한 인터뷰에서 설명했습니다. 그들은 전에 그것을들은 적이 없었고, 그 이후로 C #에 익숙하다는 것에 의문을 제기하지 않았습니다 :)
Jon Skeet

77
@Jon Skeet 비틀즈를 쓰러 뜨린 이래로 기술을 인정하지 못하는 서사시가 없었습니다. :-) 이제부터는 표지에 쓰여진 SO 프로필에 대한 URL 링크가 포함 된 책의 사본을 보내십시오.
Iain Holder

6
IainMH는 : 무엇의 가치를 위해, 나는하지 않았다 확실히 아직 책을 쓰기 시작했다. (또는 아마도 1 장에서 일하고 있었을 것입니다.) 저를 검색하면 내 블로그와 기사 등을 빨리 찾을 수 있었을 것입니다.
Jon Skeet

7
Re : 마지막 문장 q-미래 심판을 위해 SymbolHound는 symbolhound.com/?q=%3F%3F&l=&e=&n=&u= [의심스러운 사람에게-나는 관련이 없습니다. 어쨌든, 내가 하나를 찾을 때 좋은 도구처럼 ...]
Steve Chambers

답변:


2154

그것은 합체 연산자이며, 삼항 (즉시) 연산자와 매우 비슷합니다. 또한보십시오 ?? 운영자-MSDN .

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

로 확장 :

FormsAuth = formsAuth != null ? formsAuth : new FormsAuthenticationWrapper();

다음으로 확장됩니다.

if(formsAuth != null)
    FormsAuth = formsAuth;
else
    FormsAuth = new FormsAuthenticationWrapper();

영어로, "왼쪽에있는 것이 널이 아닌 경우이를 사용하고 그렇지 않으면 오른쪽에있는 것을 사용하십시오"를 의미합니다.

이 중 몇 가지를 순서대로 사용할 수 있습니다. 다음 명령문은 널 Answer#이 아닌 첫 번째를 지정합니다 Answer(모든 응답이 널인 경우에는 Answer널임).

string Answer = Answer1 ?? Answer2 ?? Answer3 ?? Answer4;

또한 위의 확장은 개념적으로 동일하지만 각 식의 결과는 한 번만 평가됩니다. 예를 들어 표현식이 부작용이있는 메소드 호출 인 경우에 중요합니다. (이 점을 지적한 @Joey에게 감사의 뜻을 전합니다.)


17
이것들을 연결하는 것이 잠재적으로 위험 할 수 있습니다
Coops

6
@CodeBlend 어떻게 그렇게?
lc.

68
@CodeBlend, 위험하지 않습니다. 확장한다면 일련의 중첩 된 if / else 문이있을 것입니다. 구문은 익숙하지 않기 때문에 이상합니다.
joelmdev

31
이 : 물론 빈 문자열 일 경우는 신 전송했을
Zyphrax

14
@Gusdor ??는 연관되어 있으므로 a ?? b ?? c ?? d와 같습니다 ((a ?? b) ?? c ) ?? d. "할당 연산자와 삼항 연산자 (? :)는 오른쪽 연관입니다. 다른 모든 이진 연산자는 연관되어 있습니다." 출처 : msdn.microsoft.com/en-us/library/ms173145.aspx
Mark E. Haase 2014

284

아무도 마법의 단어를 아직 말하지 않았기 때문에 그것은 null 통합 연산자 입니다. C # 3.0 언어 사양 의 섹션 7.12에 정의되어 있습니다 .

특히 표현식에서 여러 번 사용될 때 작동하는 방식 때문에 매우 편리합니다. 형식의 표현 :

a ?? b ?? c ?? d

anull이 아닌 경우 expression의 결과를 제공하고 b, 그렇지 않으면 try c, 그렇지 않으면 try을 제공 d합니다. 모든 지점에서 단락됩니다.

또한의 유형이 d널 입력 가능하지 않은 경우 전체 표현식의 유형도 널 입력 가능하지 않습니다.


2
"널 병합 연산자"로 그 의미를 단순화 한 방법이 마음에 듭니다. 그게 내가 필요한 전부 야
FrankO

2
접선하지만 관련 : 그것은 스위프트 지금, 그러나 매우 다르다 "무기 호 합체 운영자" developer.apple.com/library/ios/documentation/Swift/Conceptual/...
댄 Rosenstark는


27

모두들 감사합니다. MSDN 사이트에서 가장 간결한 설명은 다음과 같습니다.

// y = x, unless x is null, in which case y = -1.
int y = x ?? -1;

4
이것은 ??의 중요한 측면을 암시합니다. 연산자-널 입력 가능 유형 작업을 지원하기 위해 도입되었습니다. 귀하의 예에서 "x"는 "int?"유형입니다. (Nullable <int>).
Drew Noakes

9
@vitule no, null 병합 연산자의 두 번째 피연산자가 널 입력 가능하지 않은 경우 결과는 널 입력이 불가능합니다 (널 ( null)이 아닌 -1일반입니다 int).
수잔 듀 페론

나는 그것이 그렇게 작동하기를 정말로 바란다. 항상이 작업을 수행하고 싶지만 사용중인 변수가 nullable 유형이 아니기 때문에 할 수 없습니다. coffeescript에서 작동 y = x? -1
PandaWood

@PandaWood 실제로, 당신은 할 수 있습니다. 경우 x유형이다 int?, 그러나 y유형이다 int, 당신은 쓸 수 있습니다 int y = (int)(x ?? -1). 그것은 구문 분석 xint그렇지 않은 경우 null, 또는 지정 -1y하는 경우 x입니다 null.
Johan Wintgens

21

??값이 널일 때 널 입력 가능 유형에 값을 제공해야합니다. 따라서 formsAuth가 null이면 새 FormsAuthenticationWrapper ()를 반환합니다.


19

여기에 이미지 설명을 입력하십시오

두 개의 물음표 (??)는 해당 Coalescing 연산자를 나타냅니다.

Coalescing 연산자는 체인에서 첫 번째 NON-NULL 값을 반환합니다. 실제로 모든 것을 보여주는 이 YouTube 비디오 를 볼 수 있습니다 .

그러나 비디오의 내용을 더 추가하겠습니다.

통합의 영어 의미를 보면 "함께 통합"이라고 말합니다. 예를 들어 아래는 4 개의 문자열을 연결하는 간단한 통합 코드입니다.

그래서 경우는 str1이다 null그것을 시도 할 str2경우 str2입니다 null그것은 시도 할 것이다 str3그래서이 null이 아닌 값을 가진 문자열을 찾을 때까지합니다.

string final = str1 ?? str2 ?? str3 ?? str4;

간단히 말해 Coalescing 연산자는 체인에서 첫 번째 NON-NULL 값을 반환합니다.


나는이 설명이 다이어그램을 가지고 있기 때문에 좋아합니다. 그리고 마지막 문장은 간단한 용어로 설명합니다! 이해하기 쉽고 사용에 대한 자신감이 높아집니다. 감사!
Joshua K

14

삼항 연산자의 짧은 손입니다.

FormsAuth = (formsAuth != null) ? formsAuth : new FormsAuthenticationWrapper();

또는 삼항을하지 않는 사람들을 위해 :

if (formsAuth != null)
{
  FormsAuth = formsAuth;
}
else
{
  FormsAuth = new FormsAuthenticationWrapper();
}

3
나는 "삼항"의 철자를 수정했지만 실제로 의미하는 연산자는 조건부 연산자입니다. C #에서는 유일한 삼항 연산자이지만 어떤 시점에서 그들은 "삼항"이 모호하지만 "조건부"가 아닌 다른 것을 추가 할 수 있습니다.
Jon Skeet

삼항 (조건부) 연산자로 수행 할 수있는 작업 의 줄임말입니다 . 긴 형식으로 테스트 ( != null)와 두 번째 formsAuth(이후 ?)를 변경할 수 있습니다. 널 통합 양식에서, 둘 다 제공 한 값을 내재적으로 가져옵니다.
Bob Sammers

12

Ruby에 익숙하다면 ||=C #과 비슷합니다 ??. 다음은 루비입니다.

irb(main):001:0> str1 = nil
=> nil
irb(main):002:0> str1 ||= "new value"
=> "new value"
irb(main):003:0> str2 = "old value"
=> "old value"
irb(main):004:0> str2 ||= "another new value"
=> "old value"
irb(main):005:0> str1
=> "new value"
irb(main):006:0> str2
=> "old value"

그리고 C #에서 :

string str1 = null;
str1 = str1 ?? "new value";
string str2 = "old value";
str2 = str2 ?? "another new value";

4
x ||= ydesugars와 같은 것으로 x = x || y, ??실제로 ||Ruby의 일반과 비슷합니다 .
qerub

6
하는 것으로 ??만 걱정이 약 null의 반면 ||대부분의 언어에서와 루비의 연산자보다 약 null, false의 값을 부울 고려 될 수있다, 또는 아무것도 false(일부 언어의 예는, ""). 이것은 좋고 나쁜 것이 아니라 단지 차이입니다.
Tim S.

11

통합 연산자

그것은 동등하다

FormsAuth = formsAUth == null ? new FormsAuthenticationWrapper() : formsAuth

11

이것에 대해 위험한 것은 없습니다. 사실, 그것은 아름답습니다. 원하는 경우 기본값을 추가 할 수 있습니다 (예 :

암호

int x = x1 ?? x2 ?? x3 ?? x4 ?? 0;

1
따라서 x1, x2, x3 및 x4는 Nullable 유형이 될 수 있습니다. 예 : int? x1 = null;맞습니까
Kevin Meredith

1
@KevinMeredith는 x1- x4nullable 형식이어야한다 : 효과적으로, "결과가 말을 할 의미하지 않습니다 0경우 x4(이것은 아마도 취할 수없는 값입니다" null). 여기서 "널링 가능 유형"에는 물론 널 입력 가능 유형과 참조 유형이 모두 포함됩니다 . 하나 이상의 연결 변수 (마지막 변수 제외)가 널 입력 가능하지 않은 경우 컴파일 타임 오류입니다.
Bob Sammers

11

"널 병합 연산자"( ?? ) 라는 수많은 답변에서 올바르게 지적했듯이 , "널 조건 연산자"( ?. 또는 ? [ ) 의 사촌을 확인하고 싶을 수도 있습니다 . 여러 번 사용하면 ??

무조건 연산자

멤버 액세스 ( ?. ) 또는 인덱스 ( ? [ ) 조작을 수행하기 전에 널을 테스트하는 데 사용됩니다 . 이 연산자는 특히 데이터 구조로 내림차순으로 null 검사를 처리하는 코드를 적게 작성하도록 도와줍니다.

예를 들면 다음과 같습니다.

// if 'customers' or 'Order' property or 'Price' property  is null,
// dollarAmount will be 0 
// otherwise dollarAmount will be equal to 'customers.Order.Price'

int dollarAmount = customers?.Order?.Price ?? 0; 

없는 오래된 방법 ?. 그리고 ?? 이것을하는 것의

int dollarAmount = customers != null 
                   && customers.Order!=null
                   && customers.Order.Price!=null 
                    ? customers.Order.Price : 0; 

더 장황하고 번거 롭습니다.


10

당신의 즐거움을 위해서만 (당신은 모두 C # 사람임을 알고 있습니다 ;-).

스몰 토크 (Smalltalk)에서 유래 한 것으로 생각되며 수년 동안 존재 해 왔습니다. 여기에는 다음과 같이 정의됩니다.

객체에서 :

? anArgument
    ^ self

UndefinedObject (일명 nil 클래스)에서 :

? anArgument
    ^ anArgument

이것에 대한 평가 (?) 및 비평가 버전 (??)이 모두 있습니다.
게으른 초기화 개인 (인스턴스) 변수에 대한 getter-methods에서 종종 발견되며 실제로 필요할 때까지 0으로 남아 있습니다.


ViewControl을 UserControl의 속성으로 래핑하는 것처럼 들립니다. 이전에 설정하지 않은 경우 첫 번째 가져 오기에서만 초기화하십시오. =)
Seiti

9

여기서 병합을 사용하여 값을 얻는 몇 가지 예는 비효율적입니다.

당신이 정말로 원하는 것은 :

return _formsAuthWrapper = _formsAuthWrapper ?? new FormsAuthenticationWrapper();

또는

return _formsAuthWrapper ?? (_formsAuthWrapper = new FormsAuthenticationWrapper());

이렇게하면 매번 개체가 다시 만들어지지 않습니다. 개인 변수가 널로 남아 있고 모든 요청에서 새 오브젝트가 작성되는 대신 새 오브젝트가 작성되면 개인 변수가 지정됩니다.


바로 가기가 ??평가 되지 않습니까? nil 인 경우에만new FormsAuthenticationWrapper(); 평가 됩니다 _formsAuthWrapper .
MSalters 2016 년

예, 그게 요점입니다. 변수가 널인 경우에만 메소드를 호출하려고합니다. @MSalters
KingOfHypocrites

8

노트 :

나는이 스레드와 다른 많은 것들을 읽었지만 이만큼 완전한 대답을 찾을 수는 없습니다.

"사용해야하는 이유 및 사용시기 ?? 및 사용 방법 ??"를 완전히 이해했습니다.

출처:

Craig McMurtry의 ISBN 0-672-32948-4

널 입력 가능 값 유형

값이 값 유형의 인스턴스에 지정되었는지 여부를 알고 싶은 두 가지 일반적인 상황이 있습니다. 첫 번째는 인스턴스가 데이터베이스의 값을 나타내는 경우입니다. 이 경우 인스턴스가 데이터베이스에 실제로 값이 있는지 확인하기 위해 인스턴스를 검사 할 수 있기를 원합니다. 이 책의 주제와 관련이있는 다른 상황은 인스턴스가 일부 원격 소스에서 수신 한 데이터 항목을 나타내는 경우입니다. 다시, 인스턴스로부터 그 데이터 아이템에 대한 값이 수신되었는지를 결정하고자한다.

.NET Framework 2.0에는 값 형식의 인스턴스에 null을 할당하고 인스턴스 값이 null인지 테스트하려는 경우와 같은 경우를 제공하는 일반 형식 정의가 통합되어 있습니다. 이 제네릭 형식 정의는 System.Nullable이며 T를 값 형식으로 대체 할 수있는 제네릭 형식 인수를 제한합니다. System.Nullable에서 생성 된 유형의 인스턴스에는 null 값을 할당 할 수 있습니다. 실제로 해당 값은 기본적으로 null입니다. 따라서 System.Nullable에서 생성 된 형식은 Null 허용 값 형식이라고 할 수 있습니다. System.Nullable에는 인스턴스 값이 null이 아닌 경우 해당 형식으로 구성된 형식의 인스턴스에 할당 된 값을 얻을 수있는 속성 Value가 있습니다. 따라서 다음과 같이 작성할 수 있습니다.

System.Nullable<int> myNullableInteger = null;
myNullableInteger = 1;
if (myNullableInteger != null)
{
Console.WriteLine(myNullableInteger.Value);
}

C # 프로그래밍 언어는 System.Nullable에서 생성 된 형식을 선언하기위한 약식 구문을 제공합니다. 이 구문을 사용하면 다음과 같은 약어를 사용할 수 있습니다.

System.Nullable<int> myNullableInteger;

int? myNullableInteger;

컴파일러는 다음과 같이 널 입력 가능 값 유형의 값을 일반 값 유형에 지정하지 못하게합니다.

int? myNullableInteger = null;
int myInteger = myNullableInteger;

널 입력 가능 값 유형에 널 (NULL) 값이있을 수 있으므로 실제로는이 값을 가질 수 있으며 해당 값을 일반 값 유형에 지정할 수 없기 때문에이를 수행하지 못하게합니다. 컴파일러는이 코드를 허용하지만

int? myNullableInteger = null;
int myInteger = myNullableInteger.Value;

System.Nullable에서 생성 된 형식에 유효한 T 값이 할당되지 않은 경우 System.Nullable.Value 속성에 액세스하려는 시도가 잘못된 작업이므로 두 번째 문에서 예외가 발생합니다. 케이스.

결론:

nullable 값 형식의 값을 일반 값 형식에 할당하는 올바른 방법 중 하나는 System.Nullable.HasValue 속성을 사용하여 유효한 T 값이 nullable 값 형식에 할당되었는지 확인하는 것입니다.

int? myNullableInteger = null;
if (myNullableInteger.HasValue)
{
int myInteger = myNullableInteger.Value;
}

다른 옵션은이 구문을 사용하는 것입니다.

int? myNullableInteger = null;
int myInteger = myNullableInteger ?? -1;

일반 정수 myInteger에 널 (NULL) 입력 가능 정수 "myNullableInteger"의 값이 지정됩니다. 그렇지 않으면 myInteger에 값 -1이 할당됩니다.


1

삼항 연산자와 비슷하게 작동하는 null 병합 연산자입니다.

    a ?? b  => a !=null ? a : b 

이것에 대한 또 다른 흥미로운 점은 "널링 가능 유형은 값을 포함하거나 정의 할 수 없음" 입니다. 따라서 널 입력 가능 값 유형을 널 입력 불가능 값 유형에 지정하려고하면 컴파일시 오류가 발생합니다.

int? x = null; // x is nullable value type
int z = 0; // z is non-nullable value type
z = x; // compile error will be there.

그래서 그것을 사용하여 ?? 운영자:

z = x ?? 1; // with ?? operator there are no issues

1
FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

에 해당

FormsAuth = formsAuth != null ? formsAuth : new FormsAuthenticationWrapper();

그러나 멋진 점은 다른 사람들이 말했듯이 체인을 묶을 수 있다는 것입니다. 손대지 않은 것은 실제로 예외를 사용하는 데 사용할 수 있다는 것입니다.

A = A ?? B ?? throw new Exception("A and B are both NULL");

운영자가 무엇인지 또는 무엇인지에 대한 설명을 찾고 있었지만 게시물에 예제를 포함시키는 것이 정말 좋습니다.
TGP1994

1

??연산자를 널 병합 연산자라고합니다. 피연산자가 널이 아닌 경우 왼쪽 피연산자를 리턴합니다. 그렇지 않으면 오른쪽 피연산자를 반환합니다.

int? variable1 = null;
int variable2  = variable1 ?? 100;

NOT NULL 이면 variable2값으로 설정하십시오 . 그렇지 않으면 인 경우 100으로 설정하십시오 .variable1variable1variable1 == nullvariable2


0

다른 사람들은 Null Coalescing Operator꽤 잘 설명했습니다 . 관심있는 사람들에게는 이것이 짧은 구문이 있습니다 (SO 질문).

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

이것과 같습니다 :

FormsAuth ??= new FormsAuthenticationWrapper();

어떤 사람들은 더 읽기 쉽고 간결하다고 생각합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.