Nullable <T> .HasValue 또는 Nullable <T>! = null 중에서 선호되는 것은 무엇입니까?


437

나는 Nullable<>.HasValue의미를 좋아했기 때문에 항상 사용 했습니다. 그러나 최근에는 다른 사람의 기존 코드베이스를 사용하여 Nullable<> != null독점적으로 사용했습니다.

다른 것을 사용하는 이유가 있습니까, 아니면 순수한 선호입니까?

  1. int? a;
    if (a.HasValue)
        // ...

vs.

  1. int? b;
    if (b != null)
        // ...

9
나는 ... 비슷한 질문을 몇 가지 좋은 답변을 가지고 : stackoverflow.com/questions/633286/...
nailitdown

3
개인적으로 , 나는 HasValue단어보다 기호가 더 읽기 쉽다고 생각 하기 때문에 사용 합니다. 그것은 모두 당신에게 달려 있으며 기존 스타일에 맞는 것입니다.
Jake Petroules

1
.HasValue유형이 T?문자열과 같이 널 입력 가능 유형이 아닌 유형임을 표시하므로 더 의미 가 있습니다.
user3791372

답변:


476

컴파일러는 null 비교를에 대한 호출로 대체 HasValue하므로 실제 차이는 없습니다. 당신과 동료들에게 더 읽기 쉽고 더 이해하기 쉬운 것을하십시오.


86
나는 "기존 코딩 스타일보다 더 일관성 있고 / 따라서"라고 덧붙입니다.
Josh Lee

20
와. 나는이 구문 설탕을 싫어한다. int? x = nullnullable 인스턴스가 참조 유형이라는 환상을 얻습니다. 그러나 진실은 Nullable <T>이 값 유형이라는 것입니다. NullReferenceException이 발생한다고 생각 int? x = null; Use(x.HasValue)합니다.
KFL

11
@KFL 문법 설탕이 당신을 귀찮게한다면, Nullable<int>대신에 사용하십시오 int?.
Cole Johnson

24
응용 프로그램을 만드는 초기 단계에서는 nullable 값 형식을 사용하여 일부 데이터를 저장하는 것이 충분하다고 생각할 수 있습니다. 그런 다음 null과 비교하기 위해 원래 코드를 작성하면 HasValue ()에 대한 모든 호출을 null 비교로 검색하거나 바꿀 필요가 없다는 이점이 있습니다.
Anders

15
그것은 null로 null 허용을 설정하거나의가 주어진 null로 비교 할 수있는 불평 꽤 바보 Null 허용이라고합니다 . 문제는 사람들이 "참조 유형"을 "널 (null)이 될 수 있음"으로 혼동하고 있다는 것입니다. 그러나 그것은 개념적인 혼란입니다. 향후 C #에는 null을 허용하지 않는 참조 유형이 있습니다.
Jim Balter

48

(a != null)구문이 참조 유형과 일치하도록 선호 합니다.


11
물론 참조 유형 Nullable<>아니기 때문에 오해의 소지 가 있습니다.
Luaan

9
예, 그러나 사실은 널 점검하는 시점에서 거의 중요하지 않습니다.
cbp

31
개념적으로 혼란 스러울뿐입니다. 서로 다른 두 유형에 대해 일관된 구문을 사용한다고해서 동일한 유형임을 의미하지는 않습니다. C #에는 nullable 참조 형식 (현재는 모든 null 형식이 nullable이지만 나중에 변경 될 예정 임)과 nullable 값 형식이 있습니다. 모든 널 입력 가능 유형에 일관된 구문을 사용하는 것이 좋습니다. 결코 널 입력 가능 값 유형이 참조 유형이거나 널 입력 가능 참조 유형이 값 유형임을 의미하지는 않습니다.
Jim Balter

내가 선호 HasValue가보다 더 많은 읽을 수 있기 때문에!= null
콘라드

동일한 코드를 작성하는 다른 스타일을 혼합하지 않으면 코딩 일관성을 더 쉽게 읽을 수 있습니다. 모든 장소에 .HasValue 속성이있는 것은 아니므로 일관성 향상을 위해! = null을 사용하기 때문입니다. 내 의견에.
ColacX

21

nullable int에 값을 할당하기 위해 다른 방법을 사용하여 이것에 대한 연구를했습니다. 여러 가지 일을했을 때 일어난 일입니다. 무슨 일이 일어나고 있는지 분명히해야합니다. 명심하십시오 : Nullable<something>또는 속기 something?는 컴파일러가 마치 마치 클래스 인 것처럼 null로 사용할 수 있도록 많은 작업을 수행하는 것처럼 보입니다.
아래 볼 수로서, SomeNullable == null그리고 SomeNullable.HasValue항상이 참 또는 거짓 예상 반환합니다. 아래에 설명되어 있지는 않지만 SomeNullable == 3유효합니다 (SomeNullable이이라고 가정 int?).
하지만 SomeNullable.Value우리에게 런타임 오류를 가져옵니다 우리는 할당 된 경우 nullSomeNullable. 오버로드 된 연산자의 조합으로 인해 nullables가 문제를 일으킬 수있는 유일한 경우입니다.object.Equals(obj) 방법, 컴파일러 최적화 및 원숭이 사업.

다음은 내가 실행 한 코드와 레이블에서 생성 된 출력에 대한 설명입니다.

int? val = null;
lbl_Val.Text = val.ToString(); //Produced an empty string.
lbl_ValVal.Text = val.Value.ToString(); //Produced a runtime error. ("Nullable object must have a value.")
lbl_ValEqNull.Text = (val == null).ToString(); //Produced "True" (without the quotes)
lbl_ValNEqNull.Text = (val != null).ToString(); //Produced "False"
lbl_ValHasVal.Text = val.HasValue.ToString(); //Produced "False"
lbl_NValHasVal.Text = (!(val.HasValue)).ToString(); //Produced "True"
lbl_ValValEqNull.Text = (val.Value == null).ToString(); //Produced a runtime error. ("Nullable object must have a value.")
lbl_ValValNEqNull.Text = (val.Value != null).ToString(); //Produced a runtime error. ("Nullable object must have a value.")

좋아, 다음 초기화 방법을 시도해보십시오.

int? val = new int?();
lbl_Val.Text = val.ToString(); //Produced an empty string.
lbl_ValVal.Text = val.Value.ToString(); //Produced a runtime error. ("Nullable object must have a value.")
lbl_ValEqNull.Text = (val == null).ToString(); //Produced "True" (without the quotes)
lbl_ValNEqNull.Text = (val != null).ToString(); //Produced "False"
lbl_ValHasVal.Text = val.HasValue.ToString(); //Produced "False"
lbl_NValHasVal.Text = (!(val.HasValue)).ToString(); //Produced "True"
lbl_ValValEqNull.Text = (val.Value == null).ToString(); //Produced a runtime error. ("Nullable object must have a value.")
lbl_ValValNEqNull.Text = (val.Value != null).ToString(); //Produced a runtime error. ("Nullable object must have a value.")

이전과 동일합니다. int? val = new int?(null);nullable 객체의 VALUE은 (는) nullable이 아니므로 생성자로 null이 전달 된 상태에서을 (를) 초기화 하면 COMPILE 시간 오류가 발생합니다. 널 (null)과 같은 것은 랩퍼 오브젝트 자체입니다.

마찬가지로 다음과 같이 컴파일 시간 오류가 발생합니다.

int? val = new int?();
val.Value = null;

그것이 val.Value읽기 전용 속성 이라는 것은 말할 것도없고 , 다음과 같은 것을 사용할 수도 없습니다 :

val.Value = 3;

다시 말하지만, 다형성 오버로드 된 암시 적 변환 연산자는 다음과 같이합니다.

val = 3;

polysomthing whatchamacallits가 제대로 작동하는 한 걱정할 필요가 없습니까? :)


5
"기억하세요 : Nullable <something> 또는 속기?"는 클래스입니다. " 이것은 잘못이다! Nullable <T>는 구조체입니다. null과 비교할 때 true를 반환하기 위해 Equals 및 == 연산자를 오버로드합니다. 컴파일러는이 비교를 위해 멋진 작업을 수행하지 않습니다.
andrewjs

1
@andrewjs-당신은 그것이 구조체 (클래스가 아님)라는 것이 맞지만, == 연산자를 오버로드한다는 것은 잘못입니다. Nullable<X>VisualStudio 2013 및 F12 를 입력 하면 (와)과의 변환 XEquals(object other)메서드 에만 과부하가 발생한다는 것을 알 수 있습니다. 그러나 == 연산자는 기본적으로 해당 방법을 사용한다고 생각하므로 효과는 동일합니다. 나는 실제로이 사실에 대해이 답변을 잠시 동안 업데이트하는 것을 의미했지만, 게 으르거나 바쁘다. 이 의견은 지금해야 할 것입니다 :)
Perrin Larson

나는 ildasm을 통해 빠른 검사를했고 컴파일러가 마술을하는 것에 대해 옳았습니다. Nullable <T> 객체를 null과 비교하면 실제로 HasValue에 대한 호출로 변환됩니다. 흥미 롭습니다!
andrewjs 2016 년

3
@andrewjs 실제로, 컴파일러는 nullable을 최적화하기 위해 많은 작업을 수행합니다. 예를 들어, nullable 유형에 값을 할당하면 실제로는 nullable이 될 수 없습니다 (예 :) int? val = 42; val.GetType() == typeof(int). 따라서 nullable은 null과 같을 수있는 구조체 일뿐만 아니라 nullable이 아닌 경우도 많습니다! : D 같은 방법으로, 당신은 널 (NULL) 값을 상자에서있는 거 권투 int,하지 int?- 그리고이 때 int?값이없는, 당신은 얻을 null대신 박스 널 (NULL) 값. 기본적으로 nullable을 올바르게 사용하면 오버 헤드가 거의 발생하지 않습니다.
Luaan

1
@JimBalter 정말요? 매우 흥미 롭습니다. 그렇다면 메모리 프로파일 러는 클래스의 널 입력 가능 필드에 대해 무엇을 알려줍니까? C #의 다른 값 유형에서 상속되는 값 유형을 어떻게 선언합니까? .NET의 nullable 형식과 동일하게 동작하는 고유 한 nullable 형식을 어떻게 선언합니까? 언제부터 Null.NET의 유형입니까? CLR / C # 사양에서 해당 부분을 가리킬 수 있습니까? Nullables는 CLR 사양에 잘 정의되어 있으며 동작은 "추상화 구현"이 아니라 계약 입니다. 그러나 최선을 다하는 것이 adhominem 공격이라면 스스로 즐기십시오.
Luaan

13

VB.Net에서. ".HasValue"를 사용할 수있는 경우 "IsNot Nothing"을 사용하지 마십시오. 방금 "IsNot Nothing"을 ".HasValue"로 대체하여 "작업이 런타임을 불안정하게 할 수 있습니다"중간 신뢰 오류를 해결했습니다. 왜 그런지 이해하지 못하지만 컴파일러에서 무언가 다르게 발생합니다. C #의 "! = null"에 동일한 문제가 있다고 가정합니다.


8
HasValue가독성 때문에 선호합니다 . IsNot Nothing(이중 부정 때문에) 정말 못생긴 표현입니다.
Stefan Steinegger

12
@steffan "IsNot Nothing"은 이중 부정이 아닙니다. "아무것도"는 음수가 아니며, 프로그래밍 영역 밖에서도 이산적인 양입니다. "이 수량은 아무것도 아닙니다." 문법적으로, "이 수량은 0이 아닙니다."라고 말하는 것과 정확히 같습니다. 그리고 이중 부정도 아닙니다.
jmbpiano

5
여기에 진실이없는 것에 동의하고 싶지 않다는 것이 아니라 지금 당장 나오는 것입니다. IsNot 아무 것도 명확하고 지나치게 부정적인 것은 없습니다. HasValue와 같이 긍정적이고 명확한 것을 작성하지 않겠습니까? 이것은 문법 테스트가 아니라 코딩이며 주요 목표는 명확성입니다.
랜디 Gamage

3
jmbpiano : 이중 부정이 아니라는 점에 동의합니다. 그러나 단일 부정이며 이는 단순한 긍정적 인 표현만큼이나 추악하고 명확하지 않습니다.
Kaveh Hadjari

0

linq를 사용하고 코드를 짧게 유지하려면 항상 사용하도록 권장합니다 !=null

그리고 이것이 이유입니다.

우리는 몇 가지 클래스가 상상하자 Foo널 (NULL) 더블 변수SomeDouble

public class Foo
{
    public double? SomeDouble;
    //some other properties
}   

코드 어딘가에 Foo 컬렉션에서 null이 아닌 SomeDouble 값을 가진 모든 Foo를 가져 오려면 (컬렉션의 일부 foo도 null 일 수 있다고 가정) 함수를 작성하는 적어도 세 가지 방법으로 끝납니다. C # 6 사용) :

public IEnumerable<Foo> GetNonNullFoosWithSomeDoubleValues(IEnumerable<Foo> foos)
{
     return foos.Where(foo => foo?.SomeDouble != null);
     return foos.Where(foo=>foo?.SomeDouble.HasValue); // compile time error
     return foos.Where(foo=>foo?.SomeDouble.HasValue == true); 
     return foos.Where(foo=>foo != null && foo.SomeDouble.HasValue); //if we don't use C#6
}

이런 상황에서는 항상 더 짧은 곳으로 가라고 명령합니다


2
예, foo?.SomeDouble.HasValue해당 컨텍스트의 컴파일 시간 오류 (내 용어에서 "throw"가 아님)는 유형이 bool?단지 bool. (이 .Where방법은을 원합니다 Func<Foo, bool>.) (foo?.SomeDouble).HasValue물론 유형이 있기 때문에 수행 할 수 있습니다 bool. 이것은 첫 번째 줄이 C # 컴파일러에 의해 내부적으로 "적어도 공식적으로"번역 된 것입니다.
Jeppe Stig Nielsen

-6

일반적인 대답과 경험 법칙 : 다른 파이프 라인에서 Nullable을 처리 object하고 특정 속성을 사용 하는 옵션 (예 : 사용자 지정 serializer 작성) 이 있으면이를 수행하고 Nullable 특정 속성을 사용하십시오. 따라서 일관된 사고 관점에서 HasValue선호해야합니다. 일관된 사고는 더 많은 코드를 작성하는 데 도움이 될 수 있습니다. 예를 들어 두 번째 방법은 여러 번 더 효과적입니다 (주로 컴파일러 인라인 및 권투 때문에 여전히 숫자는 매우 표현 적입니다).

public static bool CheckObjectImpl(object o)
{
    return o != null;
}

public static bool CheckNullableImpl<T>(T? o) where T: struct
{
    return o.HasValue;
}

벤치 마크 테스트 :

BenchmarkDotNet=v0.10.5, OS=Windows 10.0.14393
Processor=Intel Core i5-2500K CPU 3.30GHz (Sandy Bridge), ProcessorCount=4
Frequency=3233539 Hz, Resolution=309.2587 ns, Timer=TSC
  [Host] : Clr 4.0.30319.42000, 64bit RyuJIT-v4.6.1648.0
  Clr    : Clr 4.0.30319.42000, 64bit RyuJIT-v4.6.1648.0
  Core   : .NET Core 4.6.25009.03, 64bit RyuJIT


        Method |  Job | Runtime |       Mean |     Error |    StdDev |        Min |        Max |     Median | Rank |  Gen 0 | Allocated |
-------------- |----- |-------- |-----------:|----------:|----------:|-----------:|-----------:|-----------:|-----:|-------:|----------:|
   CheckObject |  Clr |     Clr | 80.6416 ns | 1.1983 ns | 1.0622 ns | 79.5528 ns | 83.0417 ns | 80.1797 ns |    3 | 0.0060 |      24 B |
 CheckNullable |  Clr |     Clr |  0.0029 ns | 0.0088 ns | 0.0082 ns |  0.0000 ns |  0.0315 ns |  0.0000 ns |    1 |      - |       0 B |
   CheckObject | Core |    Core | 77.2614 ns | 0.5703 ns | 0.4763 ns | 76.4205 ns | 77.9400 ns | 77.3586 ns |    2 | 0.0060 |      24 B |
 CheckNullable | Core |    Core |  0.0007 ns | 0.0021 ns | 0.0016 ns |  0.0000 ns |  0.0054 ns |  0.0000 ns |    1 |      - |       0 B |

벤치 마크 코드 :

public class BenchmarkNullableCheck
{
    static int? x = (new Random()).Next();

    public static bool CheckObjectImpl(object o)
    {
        return o != null;
    }

    public static bool CheckNullableImpl<T>(T? o) where T: struct
    {
        return o.HasValue;
    }

    [Benchmark]
    public bool CheckObject()
    {
        return CheckObjectImpl(x);
    }

    [Benchmark]
    public bool CheckNullable()
    {
        return CheckNullableImpl(x);
    }
}

https://github.com/dotnet/BenchmarkDotNet 이 사용되었습니다

추신 . 사람들은 "일관된 사고로 인해 HasValue를 선호한다"는 조언은 관련이없고 쓸모가 없다고 말합니다. 이것의 성능을 예측할 수 있습니까?

public static bool CheckNullableGenericImpl<T>(T? t) where T: struct
{
    return t != null; // or t.HasValue?
}

PPS 사람들은 계속 마이너스를 유지하지만 아무도의 성과를 예측하려고하지 CheckNullableGenericImpl않습니다. 그리고 컴파일러는로 대체 !=null하는 데 도움이되지 않습니다 HasValue. HasValue성능에 관심이있는 경우 직접 사용해야합니다.


2
귀하의 CheckObjectImpl 상자 는으로 null을 허용 object하지만 CheckNullableImpl권투는 사용하지 않습니다. 따라서 비교는 매우 불쾌합니다. 수락 된 답변 에서 언급했듯이 컴파일러 !=HasValue어쨌든 다시 작성 하기 때문에 운임이 아니라 쓸모가 없습니다 .
GSerg

2
독자는의 구조체 자연 무시하지 않는다 Nullable<T>, 당신이을 (AN으로 권투에 의해 object). != null왼쪽에서 nullable 을 적용 !=하면 컴파일러 수준에서 nullable 에 대한 지원이 작동 하므로 권투가 발생하지 않습니다 . 컴파일러에서 nullable을 먼저 상자에 넣어서 숨길 때 다릅니다 object. 어느 쪽도하지 CheckObjectImpl(object o)않고 원칙적으로 벤치 마크 메이크 감각.
GSerg

3
내 문제는이 웹 사이트의 콘텐츠 품질에 관심이 있다는 것입니다. 게시 한 내용이 잘못되었거나 잘못되었습니다. 당신은 OP의 질문에 대답을 시도한다면, 당신의 대답은 대체하여 증명하기 쉬운 플랫 아웃 잘못입니다 전화CheckObjectImpl그와 신체 내부 CheckObject. 그러나 귀하의 최근 의견에 따르면 8 년 된이 질문에 대답하기로 결정했을 때 실제로는 완전히 다른 질문을 염두에 두었으므로 원래 질문의 맥락에서 답이 오도됩니다. OP가 요구 한 것은 아닙니다.
GSerg

3
구글 다음 사람의 신발에 자신을 넣어 what is faster != or HasValue. 그는이 질문에 답하고, 답을 찾아보고, 벤치 마크에 감사하며, "저기, !=너무 느리기 때문에 절대 사용하지 않겠 습니다!"라고 말합니다. 그것은 그가 틀렸다는 결론을 내릴 매우 잘못된 결론입니다. 그래서 나는 당신의 대답이 해롭다 고 믿습니다. 그것은 잘못된 질문에 대답하고 따라서 의심의 여지가없는 독자에게 잘못된 결론을 심습니다. 당신이 당신을 변경할 때 발생하는 고려 CheckNullableImpl하는 return o != null;같은 벤치 마크 결과를 얻을 것이다.
GSerg

8
나는 당신의 대답으로 논쟁하고 있습니다. 이 차이점을 보여줍니다처럼 당신의 대답은 믿을 외모 !=HasValue는 차이점을 보여줍니다 사실 object oT? o. 당신은 내가 무엇을 제안 할 경우, 즉, 다시 CheckNullableImplpublic static bool CheckNullableImpl<T>(T? o) where T: struct { return o != null; }당신은 벤치 마크와 함께 종료됩니다, 그 명확하게 보여줍니다 !=훨씬 낮은 속도보다 !=. 귀하의 답변이 설명하는 문제가 전혀 !=대립 되지 않았다는 결론을 내릴 수 HasValue있습니다.
GSerg
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.