C #의 속성 통합 연산자


9

C #의 null-coalescing 연산자를 사용하면 코드를 줄일 수 있습니다

  if (_mywidget == null)
     return new Widget();
  else
     return _mywidget;

아래로:

  return _mywidget ?? new Widget();

C #에서 갖고 싶은 유용한 연산자는 객체의 속성을 반환하거나 객체가 null 인 경우 다른 값을 반환 할 수있는 연산자입니다. 교체하고 싶습니다

  if (_mywidget == null)
     return 5;
  else
     return _mywidget.Length;

와:

  return _mywidget.Length ??! 5;

이 연산자가 존재하지 않는 이유가 있다고 생각할 수 없습니다. 코드 냄새입니까? 이것을 작성하는 더 좋은 방법이 있습니까? (null 객체 패턴을 알고 있지만이 4 줄의 코드를 바꾸는 데 과도하게 사용됩니다.)


1
조건부 연산자로 충분합니까?
아논.

1
누군가 다음과 같이 할 수있는 것을 작성했습니다 : string location = employee.office.address.location ?? "알 수 없는"; . 이것은 오브젝트 중 하나 ( employee , office , address 또는 location )가 널인 경우 location"알 수 없음"으로 설정 합니다 . 불행히도, 누가 그것을 썼는지 또는 어디에 게시했는지 기억이 나지 않습니다. 다시 찾으면 여기에 게시하겠습니다!
Kristof Claes

1
이 질문은 StackOverflow에서 훨씬 더 많은 견인력을 얻습니다.
직업

2
??!C ++의 연산자입니다. :-)
James McNellis

3
안녕하세요 2011, 방금 C #이 안전한 내비게이션 연산자를
Nathan Cooper

답변:


5

필자는 각각의 null을 확인하지 않고도 x.Prop.SomeOtherProp.ThirdProp을 안전하게 수행 할 수있는 C # 언어 기능을 원합니다. 이러한 종류의 "딥 속성 순회"를 많이 수행해야하는 한 코드베이스에서 NullReferenceExceptions를 삼킨 Navigate라는 확장 메서드를 작성하고 대신 기본값을 반환했습니다. 그래서 나는 이런 식으로 할 수 있습니다 :

var propVal = myThing.Navigate(x => x.PropOne.PropTwo.PropThree.PropFour, defaultValue);

이것의 단점은 코드 냄새가 다르다는 것입니다. 삼키는 예외. 이와 같은 "올바른"작업을 원한다면 lamdba를 표현식으로 가져 와서 각 속성 접근 자 주위에 null 검사를 추가하도록 표현식을 즉시 수정할 수 있습니다. 나는 "빠르고 더러워"갔고 이것을 "더 나은"방법으로 구현하지 않았다. 그러나 여분의 사고 주기가있을 때 이것을 다시 방문하여 어딘가에 게시 할 것입니다.

귀하의 질문에 더 직접 대답하기 위해, 이것이 기능이 아닌 이유는 기능 구현 비용이 기능의 이점을 초과하기 때문이라고 생각합니다. C # 팀은 초점을 맞출 기능을 선택하고 선택해야하며,이 기능은 아직 맨 위에 표시되지 않았습니다. 내부 정보가 없지만 추측합니다.


1
정확히 내가 생각한 것이지만 안전한 방법의 성능은 꽤 나쁠 것입니다 (많은 Expression.Compile () 때문에).
Guillaume86

x.PropOne.PropTwo.PropThree.PropFour는 Demeter의 법칙을 위반하므로 설계 선택이 좋지 않습니다. 이것을 사용할 필요가 없도록 메소드 / 클래스를 다시 디자인하십시오.
Justin Shield

데메테르 법칙에 비추어 깊이 재산 접근을 피하는 것은 무의식적으로 데이터베이스를 정규화하는 것만 큼 위험합니다. 너무 멀리 갈 수 있습니다.
Mir

3
C # 6.0이 널 조건 연산자 (엘비스 일명 연산자)를 사용하여 가능하다 msdn.microsoft.com/en-us/magazine/dn802602.aspx을
victorvartan

15

나는 당신이 C # 6으로 이것을 아주 간단하게 지금 할 수 있다고 믿는다.

return _mywidget?.Length ?? 5;

?.왼쪽 의 null 조건부 연산자 를 참고하십시오 . _mywidget?.Lengthnull의 경우 _mywidget는 null를 돌려줍니다 .

그러나 다른 사람들이 제안한 것처럼 간단한 삼항 연산자를 쉽게 읽을 수 있습니다.


9

그들이 왜 그렇게하지 않았는 지에 대한 추측 일뿐입니다. 결국, 나는 믿지 않는다 ?? 첫 번째 C # 버전에있었습니다.

어쨌든 조건부 연산자를 사용하여 하루에 호출합니다.

return (_mywidget != null) ? _mywidget.Length : 5;

?? 조건부 연산자에 대한 바로 가기입니다.


5
개인적으로, 이것은 운영자가 처음에 이것을하는 것만큼이나 나쁘다 (imho). 당신은 객체에서 무언가를 원합니다 ... 그 객체가 없을 수도 있습니다 ... 그래서 5거기에없는 경우에 대한 답변을 제공합시다 . 특수 연산자를 요청하기 전에 디자인을 살펴 봐야합니다.
Moo-Juice

1
@ Moo-Juice 나는이 코드를 관리하는 사람을 상상하고있다. 흠. 뭐?!'
msarchet 2016 년

4

삼항 연산자처럼 보입니다.

return (_mywidget != NULL) ? _mywidget : new Widget();

3

개인적으로, 나는 그것이 가독성에 달려 있다고 생각합니다. 첫 번째 예에서?? 번역은 "Are you there ??

두 번째 예에서는 ??!분명히 WTF이며 프로그래머에게는 아무 의미가 없습니다. ... 객체가 존재하지 않으므로 속성을 얻을 수 없으므로 5를 반환합니다. 그 의미는 무엇입니까? 5는 무엇입니까? 5가 좋은 가치라는 결론에 어떻게 도달합니까?

요컨대, 첫 번째 예는 의미가 있습니다 ... 거기가 아니라 새로운 것입니다. 두 번째로 토론의 여지가 있습니다.


2
글쎄, 당신은 사실을 무시해야합니까 ??! 존재하지 않습니다. 만약 그렇다면 상상 해보세요! 일반적이고 사용 된 경우이를 바탕으로 결정을 내립니다.
whatsisname

3
즉 (이 실제로 작동 : C ++에서 소위 "WTF 연산자"를 생각 나게 (foo() != ERROR)??!??! cerr << "Error occurred" << endl;.)
타마스 Szelei

@whatsisname, 그것은 의사 결정에 적합하다는 사실에 더 반영되었습니다. 객체가 존재하지 않았기 때문에 생성하는 것이 합리적입니다. 무언가 의 속성 을 얻을 수 없기 때문에 독자에게 아무것도 의미하지 않는 수차 값을 할당하는 것은 실제로 WTF 시나리오입니다.
Moo-Juice

내 예에서 당신이 붙잡힌 것 같아요. 아마도 0이 5보다 낫습니다. 아마도 속성이 객체이므로 _mywidget이 null이면 새 foodle ()을 반환하려고합니다. 나는 완전히 동의하지 않는다 ?? ??보다 더 읽기 쉽습니다! 그래도 :)
벤 Fulton

@Ben, 나는 연산자 자체에 초점을 맞추는 것이 귀하의 질문에 너무 많이 도움이되지 않는다는 것에 동의하지만, 프로그래머의 인식과 그 임의의 것에 평행을 이룹니다 5. 나는 실제로 이와 같은 일을 해야하는 문제가 더 많이 있다고 생각하지만, 첫 번째 예에서는 특히 캐시 관리자와 같은 것들이 필요합니다.
Moo-Juice

2

나는 사람들이 당신이 돌아 오는 "5"에 너무 갇히고 있다고 생각합니다 ... 아마도 0이 더 좋았을 것입니다 :)

어쨌든 문제는 ??!실제로 독립 실행 형 연산자가 아니라는 것입니다. 이것이 무엇을 의미하는지 고려하십시오.

var s = myString ??! "";

이 경우에는 의미가 없습니다. 왼쪽 피연산자가 속성 접근자인 경우에만 의미가 있습니다. 아니면 이것에 대해 :

var s = Foo(myWidget.Length) ??! 0;

거기에 속성 접근이다,하지만 난 여전히 (경우에 의미가 있다고 생각하지 않는 myWidget것입니다 null, 평균 우리가 전화를하지 않는 것이 않는 Foo()전혀?), 또는이 그냥 오류가?

문제는 언어만큼 자연스럽게 맞지 ??않는다는 것입니다.


1

누군가 당신을 위해 이것을 할 유틸리티 클래스를 만들었습니다. 그러나 나는 그것을 찾을 수 없습니다. 내가 찾은 것은 MSDN 포럼과 비슷한 것입니다 (두 번째 답변을보십시오).

약간의 작업으로 샘플을 지원하지 않는 메소드 호출 및 기타 표현식을 평가하도록 확장 할 수 있습니다. 기본값을 허용하도록 확장 할 수도 있습니다.


1

나는 당신이 어디에서 왔는지 이해합니다. 당신이 제공 한 예를 들어 모든 사람이 악셀을 감싸고있는 것처럼 보입니다. 나는 마법의 숫자가 나쁜 아이디어라는 것에 동의하지만, 특정 속성에 대해 null 병합 연산자와 동등한 기능을 사용할 것입니다.

예를 들어, 객체가 맵에 바인딩되어 있고 적절한 높이에 있어야합니다. DTED 맵은 데이터에 구멍이있을 수 있으므로 -100에서 ~ 8900 미터 범위의 값뿐만 아니라 null 값을 가질 수 있습니다. 다음 라인을 따라 무언가를 원할 수 있습니다.

mapObject.Altitude = mapObject.Coordinates.DtedAltitude ?? DEFAULT_ALTITUDE;

이 경우 좌표가 아직 설정되지 않았거나 좌표 객체가 해당 위치에 대한 DTED 데이터를로드 할 수없는 경우 null 병합 연산자는 기본 고도를 채 웁니다.

나는 이것이 매우 귀중한 것으로 보지만 그것이 왜 수행되지 않은지에 대한 나의 추측은 컴파일러 복잡성으로 제한됩니다. 동작이 예측할 수없는 경우가있을 수 있습니다.


1

깊게 중첩 된 것을 다루지 않을 때 이것을 사용했습니다 (C # 6에서 소개 된 null 병합 연산자 이전). 나에게는 꽤 분명하지만 어쩌면 내가 익숙하기 때문에 다른 사람들이 혼란 스러울 수 있습니다.

return ( _mywidget ?? new MyWidget() {length = defaultLength}).Length;

물론, 때로는 액세스해야 할 속성을 직접 설정할 수 없거나 개체 자체의 구성이 비싸기 때문에 항상 적용 가능한 것은 아닙니다.

또 다른 대안은 NullObject 패턴 을 사용하는 것 입니다 . 적절한 기본값을 사용하여 클래스의 단일 정적 인스턴스를 정의하고 대신 사용하십시오.

return ( _mywidget ?? myWidget.NullInstance).Length;

0

매직 넘버는 일반적으로 나쁜 냄새입니다. 5가 임의의 숫자이면 더 명확하게 문서화되도록 철자를 쓰는 것이 좋습니다. 제 생각에는 특정 상황에서 기본값으로 작동하는 값 모음이있는 경우는 예외입니다.

객체에 대한 기본값 세트가있는 경우 서브 클래 싱하여 기본 싱글 톤 인스턴스를 만들 수 있습니다.

같은 것 : return (myobj ?? default_obj). 길이


0

길이 속성은 일반적으로 값 형식이므로 null 일 수 없으므로 null 병합 연산자는 여기서 의미가 없습니다.

그러나 참조 유형 인 속성을 사용하여이를 수행 할 수 있습니다. var window = App.Current.MainWindow ?? new Window();


이 예는 Current가 null 인 경우 상황에서 발생할 수있는 상황을 고려하려고합니다. 귀하의 예는 단지 충돌 할 것입니다 ...하지만 잘못된 방향 체인의 아무 곳이나 널 병합 할 수있는 연산자가 있으면 편리합니다.
Mir

-1

전에 비슷한 아이디어를 가진 사람을 보았지만 대부분의 경우 새로운 값을 null 필드에 할당하려고합니다.

return _obj ?? (_obj = new Class());

아이디어는 ??=과제를 다음 과 같이 결합하는 것이 었 습니다.

return _obj ??= new Class();

느낌표를 사용하는 것보다 이것이 더 의미가 있다고 생각합니다.

이 아이디어는 내 것이 아니지만 정말 좋아합니다. :)


1
당신은 가난한 모범의 희생자입니까? 여기에 return _obj ?? new Class();필요가 없어서 예제를 줄일 수 있습니다 ??=.
매트 엘렌

@ 매트 엘렌 당신이 잘못했습니다. 과제는 의도 된 것 입니다. 다른 대안을 제안하고 있습니다. OP가 요청한 것과 정확히 같지는 않지만 유용 할 것이라고 생각합니다.
chakrit

2
값을 반환하려고 할 때 할당을 원하는 이유는 무엇입니까?
매트 엘렌

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