널 병합 연산자에 "반대"가 있습니까? (… 어떤 언어로?)


92

널 병합은 대략 다음과 같이 변환됩니다. return x, unless it is null, in which case return y

나는 종종 필요 return null if x is null, otherwise return x.y

나는 사용할 수있다 return x == null ? null : x.y;

나쁘지는 않지만 null중간에있는 것이 항상 나를 괴롭 힙니다. 불필요한 것 같습니다. return x :: x.y;다음과 같은 것을 선호합니다. 다음에 오는 ::것이 앞에 오는 것이 아닌 경우에만 평가됩니다 null.

I는 다음과 같이 볼이 거의 간결한 인라인 널 검사와의 혼합의 종류, 널 합체에 반대하지만 [요 C에서 # 이러한 조작이 없다고 특정한.

그러한 연산자가있는 다른 언어가 있습니까? 그렇다면 무엇이라고 부릅니까?

(저는 C #으로 메서드를 작성할 수 있다는 것을 알고 있습니다. 저는를 사용 return NullOrValue.of(x, () => x.y);하지만 더 좋은 것이 있다면 저도보고 싶습니다.)


일부는 C #에서 x? .y와 같은 것을 요청했지만 그런 것은 없습니다.
Anthony Pegram

1
@Anthony 오, 아름답습니다. 감사.
Jay는

2
C ++에서는 return x ? x.y : NULL. 포인터 유형을 부울로 변환 해주셔서 감사합니다!
Phil Miller

1
그 @Novelocrat 나 대부분의 C에서 #은 그 (아무것도) 경우가 때를 제외하고 진정한 = 경우에 그들은 C를 따르지 않은 것입니다 자극하는 것들 중 하나입니다 (0, 거짓, 널 (null))의 경우
크리스 Marisic

2
@Chris : C에 대한 정확한 설명이 아닙니다. 스칼라가 아닌 변수 (예 : 구조체)가있는 경우 조건에서 사용할 수 없습니다.
Phil Miller

답변:


62

Groovy 에는 null-safe 역 참조 연산자 (?.)가 있습니다. 그게 당신이 추구하는 것 같아요.

( 안전 탐색 연산자 라고도합니다 .)

예를 들면 :

homePostcode = person?.homeAddress?.postcode

는 null을 경우 줄 것이다 person, person.homeAddress또는 person.homeAddress.postcodenull입니다.

(이제 C # 6.0에서 사용할 수 있지만 이전 버전에서는 사용할 수 없습니다.)


1
Groovy는 또한 "Elvis operator"를 가지고 있는데, 이것은 다른 기본값을 허용합니다 null. 예 :def bar = foo ?: "<null>"
Craig Stuntz

28
이 기능을 C # 5.0에 추천합니다. 나는 Groovy가 실제로 무엇인지 모르거나 신경 쓰지 않지만 이것은 어떤 언어로도 사용할 수있을만큼 직관적입니다.
György Andrasek

그러나 지금은 C # 6에 추가되고 있습니다.
Jeff Mercado

1
안전 탐색은 게시 된 사소한 예제에서 작동하지만 함수 호출에서 null이 아닌 값을 사용하려는 경우 여전히 삼항 연산자를 사용해야합니다 (예 : Decimal? post = pre == null ? null : SomeMethod( pre );. "Decimal? post = pre :: SomeMethod (pre);"로 줄일 수 있다면 좋을 것입니다.
다이

@Dai : 당신이 원하는 순열의 수를 감안할 때, 나는 우리가 가진 것에 충분히 만족합니다.
Jon Skeet

36

?를 추가하는 것을 고려했습니다. C # 4로 전환했습니다. 그것은 "갖고있는"기능이 아니라 "갖는 것이 좋은"기능입니다. 우리는 가상의 미래 버전에 대해 다시 고려할 것이지만, 내가 당신이라면 숨을 참지 않을 것입니다. 시간이 지남에 따라 더 이상 중요하지 않을 것입니다. :-)


기능 구현의 용이성이 추가 결정에 영향을 미칩니 까? 그 연산자를 구현하는 것이 언어에 매우 간단하고 간단한 추가처럼 보이기 때문에 이것을 묻습니다. 나는 전문가가 아니므로 (C #에 대한 큰 애정을 가진 최근 졸업생) 나를 수정하십시오!
Nick Strupat 2011 년

14
@nick : 물론, 렉서와 파서를 구현하는 데 5 분 정도 걸립니다. 그런 다음 "IntelliSense 엔진이 잘 처리합니까?"와 같은 문제에 대해 걱정하기 시작합니다. ""를 입력했지만 아직.을 입력하지 않았을 때 오류 복구 시스템이 어떻게 처리합니까? " 그리고 "." 어쨌든 C #에서 의미하며, 그들 중 얼마나 많은 사람들이? 그 전에 오류 메시지는 무엇이며,이 기능이 얼마나 많은 테스트를 필요로하는지 (힌트 : 많이) 그리고 어떻게 문서화하고 변경 사항을 전달할 것인가, 그리고 ...
Eric Lippert 2011 년

3
@nick : 귀하의 질문에 답하기 위해-예, 구현 비용은 요소이지만 작은 요소입니다. 우리가 일하는 수준에는 값싼 기능이 없으며 다소 비싼 기능 만 있습니다. 올바른 코드 케이스에서 파서가 작동하도록하기위한 5 달러 가치의 개발 작업은 수만 달러 가치의 설계, 구현, 테스트, 문서화 및 교육으로 쉽게 전환 될 수 있습니다.
Eric Lippert 2011 년

11
@EricLippert 저는 이것이 C #을 매우 성공적으로 만들었고 가능한 한 간결하고 표현력있는 코드를 만드는 임무를 완벽하게 수행 할 수 있었던 주요 "가지고 있으면 좋은"중 하나라고 생각합니다!
Konstantin

: 나는 내가 정말 원하는 것은 엘비스의 운영자이며,이 추가보고 싶다 stackoverflow.com/questions/2929836/...
크리스 Marisic

16

특별한 종류의 단락 부울 논리가있는 경우 다음을 수행 할 수 있습니다 (자바 스크립트 예제).

return x && x.y;

경우 xnull의 경우, 다음은 평가하지 않습니다 x.y.


2
이것도 0, ""및 NaN에서 단락되므로 ??의 반대가 아닙니다. ||의 반대입니다.
ritaj

7

이것을 답으로 추가하는 것이 옳다고 느꼈습니다.

C #에 그런 것이없는 이유는 병합 연산자 (참조 형식에만 유효 함)와 달리 역방향 작업이 참조 또는 값 형식 (즉 class x, 멤버 사용)을 생성 int y할 수 있기 때문이라고 생각합니다. 많은 상황에서 사용할 수 없습니다.

그러나 나는 그것을보고 싶지 않다고 말하는 것이 아닙니다!

이 문제에 대한 잠재적 인 해결책은 연산자가 오른쪽에있는 값 형식 식을 nullable로 자동으로 들어 올리는 것입니다. 그러나 x.yy가 int 인 경우 실제로 int?는 고통스러운 an 을 반환 한다는 문제 가 있습니다.

아마도 더 나은 또 다른 해결책은 연산자가 왼쪽의 표현식이 null 인 경우 오른쪽의 유형에 대한 기본값 (즉, null 또는 0)을 반환하는 것입니다. 그러나 0 / null을 실제로 읽은 시나리오 x.y또는 안전 액세스 연산자가 제공했는지 여부를 구분하는 데 문제가 있습니다 .


OP의 질문을 처음 읽었을 때이 정확한 문제가 내 머릿속에 떠 올랐지 만 어떻게 표현할 수 있을지 정하지 못했습니다. 매우 심각한 문제입니다. +1
rmeador

심각한 문제는 아닙니다. int?기본 반환 값으로 사용 하면 사용자가 원하는 경우 int로 변경할 수 있습니다. 예를 들어 Lookup, 내 참조 중 하나 가 다음과 같은 경우 기본값 -1을 사용하여 일부 메서드를 호출하려는 경우 null:foo?.Bar?.Lookup(baz) ?? -1
Qwertie

방법 갖는 약 ?. :우측 중간에 주어진 적절한 부재와 같은 형태 일 것이 요구된다 삼원 연산자를 수?
supercat 2013-06-19

6

Delphi에는 null-safe 인 : (.이 아닌) 연산자가 있습니다.

그들은?를 추가하는 것에 대해 생각하고있었습니다. 연산자를 C # 4.0으로 전환하여 동일한 작업을 수행했지만 그에 따른 블록이 있습니다.

그 동안 가려운 스크래치가있는 IfNotNull () 이 있습니다. 확실히?보다 큽니다. 또는 : 그러나 멤버 중 하나가 null 인 경우 NullReferenceException을 발생시키지 않는 일련의 작업을 구성 할 수 있습니다.


이 연산자를 사용하는 예를 들어 주시겠습니까?
Max Carroll

1
이것은 ?.C # 6.0 언어 기능이며 예, OP가 여기에서 요청한 것을 정확히 수행합니다. 더 나은 말 ^^ 결코 이상
T_D

3

Haskell에서는 >>연산자를 사용할 수 있습니다 .

  • Nothing >> Nothing 이다 Nothing
  • Nothing >> Just 1 이다 Nothing
  • Just 2 >> Nothing 이다 Nothing
  • Just 2 >> Just 1 이다 Just 1

3

Haskell fmap은이 경우에 Data.Maybe.map. Haskell은 순전히 기능적이므로 찾고있는 것은

fmap select_y x

경우 x이며 Nothing,이 돌아갑니다 Nothing. 경우 x이며 Just object,이 돌아갑니다 Just (select_y object). 점 표기법만큼 예쁘지는 않지만 기능적 언어라는 점을 감안할 때 스타일이 다릅니다.


2

PowerShell을 사용하면 null 참조에 대한 속성 (메서드 호출 아님)을 참조 할 수 있으며 인스턴스가 null 인 경우 null을 반환합니다. 이 작업은 어느 깊이에서나 할 수 있습니다. C # 4의 동적 기능이이를 지원하기를 바랐지만 지원하지 않습니다.

$x = $null
$result = $x.y  # $result is null

$x = New-Object PSObject
$x | Add-Member NoteProperty y 'test'
$result = $x.y  # $result is 'test'

예쁘지는 않지만 설명하는 방식으로 작동하는 확장 메서드를 추가 할 수 있습니다.

public static TResult SafeGet<T, TResult>(this T obj, Func<T, TResult> selector) {
    if (obj == null) { return default(TResult); }
    else { return selector(obj); }
}

var myClass = new MyClass();
var result = myClass.SafeGet(x=>x.SomeProp);

2
public class ok<T> {
    T s;
    public static implicit operator ok<T>(T s) { return new ok<T> { s = s }; }
    public static implicit operator T(ok<T> _) { return _.s; }

    public static bool operator true(ok<T> _) { return _.s != null; }
    public static bool operator false(ok<T> _) { return _.s == null; }
    public static ok<T> operator &(ok<T> x, ok<T> y) { return y; }
}

나는 종종 문자열에 대해 다음 논리가 필요합니다.

using ok = ok<string>;

...

string bob = null;
string joe = "joe";

string name = (ok)bob && bob.ToUpper();   // name == null, no error thrown
string boss = (ok)joe && joe.ToUpper();   // boss == "JOE"

1

멤버에 대한 모든 올바른 기본값을 사용하여 클래스의 정적 인스턴스를 어딘가에 만듭니다.

예를 들면 :

z = new Thingy { y=null };

다음 대신에

return x != null ? x.y : null;

당신은 쓸 수 있습니다

return (x ?? z).y;

나는 때때로 그 기술을 사용하지만 (예 : (x ?? "") .ToString ()) POD 및 문자열과 같은 몇 가지 데이터 유형에서만 실용적입니다.
Qwertie 2012-06-07


1

소위 "null 조건부 연산자"는 C # 6.0 및 Visual Basic 14에 도입되었습니다
. 많은 상황에서이 연산자는 Null 병합 연산자의 정반대로 사용할 수 있습니다.

int? length = customers?.Length; // null if customers is null   
Customer first = customers?[0];  // null if customers is null  
int? count = customers?[0]?.Orders?.Count();  // null if customers, the first customer, or Orders is null

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-conditional-operators

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