나는 Nullable<>.HasValue
의미를 좋아했기 때문에 항상 사용 했습니다. 그러나 최근에는 다른 사람의 기존 코드베이스를 사용하여 Nullable<> != null
독점적으로 사용했습니다.
다른 것을 사용하는 이유가 있습니까, 아니면 순수한 선호입니까?
-
int? a; if (a.HasValue) // ...
vs.
-
int? b; if (b != null) // ...
답변
컴파일러는 null 비교를에 대한 호출로 대체 HasValue
하므로 실제 차이는 없습니다. 당신과 동료들에게 더 읽기 쉽고 더 이해하기 쉬운 것을하십시오.
답변
(a != null)
구문이 참조 유형과 일치하도록 선호 합니다.
답변
nullable int에 값을 할당하기 위해 다른 방법을 사용하여 이것에 대한 연구를했습니다. 여러 가지 일을했을 때 일어난 일입니다. 무슨 일이 일어나고 있는지 분명히해야합니다. 명심하십시오 : Nullable<something>
또는 속기 something?
는 컴파일러가 마치 마치 클래스 인 것처럼 null로 사용할 수 있도록 많은 작업을 수행하는 것처럼 보입니다.
아래 볼 수로서, SomeNullable == null
그리고 SomeNullable.HasValue
항상이 참 또는 거짓 예상 반환합니다. 아래에 설명되어 있지는 않지만 SomeNullable == 3
유효합니다 (SomeNullable이이라고 가정 int?
).
하지만 SomeNullable.Value
우리에게 런타임 오류를 가져옵니다 우리는 할당 된 경우 null
에 SomeNullable
. 오버로드 된 연산자의 조합으로 인해 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가 제대로 작동하는 한 걱정할 필요가 없습니까? 🙂
답변
VB.Net에서. “.HasValue”를 사용할 수있는 경우 “IsNot Nothing”을 사용하지 마십시오. 방금 “IsNot Nothing”을 “.HasValue”로 대체하여 “작업이 런타임을 불안정하게 할 수 있습니다”중간 신뢰 오류를 해결했습니다. 왜 그런지 이해하지 못하지만 컴파일러에서 무언가 다르게 발생합니다. C #의 “! = null”에 동일한 문제가 있다고 가정합니다.
답변
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
}
이런 상황에서는 항상 더 짧은 곳으로 가라고 명령합니다
답변
일반적인 대답과 경험 법칙 : 다른 파이프 라인에서 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
성능에 관심이있는 경우 직접 사용해야합니다.