값 유형을 null과 비교해도 괜찮은 C #
나는 오늘 이것을 만났고 C # 컴파일러가 오류를 던지지 않는 이유를 모릅니다.
Int32 x = 1;
if (x == null)
{
Console.WriteLine("What the?");
}
x가 어떻게 null이 될 수 있는지 혼란 스럽습니다. 특히이 할당은 확실히 컴파일러 오류를 던지기 때문에 :
Int32 x = null;
x가 null이 될 수 있습니까? Microsoft가이 검사를 컴파일러에 넣지 않기로 결정 했습니까, 아니면 완전히 놓쳤습니까?
업데이트 :이 기사를 작성하기 위해 코드를 엉망으로 만든 후 갑자기 컴파일러가 표현식이 결코 사실이 아닐 것이라는 경고를 내보냈습니다. 이제 나는 정말 길을 잃었습니다. 나는 객체를 클래스에 넣었고 이제 경고가 사라졌지 만 질문이 남았습니다. 값 유형이 null이 될 수 있습니까?
public class Test
{
public DateTime ADate = DateTime.Now;
public Test ()
{
Test test = new Test();
if (test.ADate == null)
{
Console.WriteLine("What the?");
}
}
}
연산자 과부하 해결에는 선택할 수있는 고유 한 최상의 연산자가 있기 때문에 이는 합법적입니다. 두 개의 nullable int를 취하는 == 연산자가 있습니다. int local은 nullable int로 변환 할 수 있습니다. null 리터럴은 nullable int로 변환 할 수 있습니다. 따라서 이것은 == 연산자의 합법적 인 사용이며 항상 거짓이됩니다.
마찬가지로, "if (x == 12.6)"이라고 말하면 항상 거짓이됩니다. int local은 double로 변환 가능하고 리터럴은 double로 변환 가능하며 분명히 같지 않을 것입니다.
( int?
) 변환 이 있기 때문에 오류가 아닙니다 . 주어진 예에서 경고를 생성합니다.
'int'유형의 값이 'int?'유형의 'null'과 같지 않으므로 표현식의 결과는 항상 'false'입니다.
IL을 확인하면 도달 할 수없는 분기가 완전히 제거 되었음을 알 수 있습니다. 릴리스 빌드에는 존재하지 않습니다.
그러나 같음 연산자가있는 사용자 지정 구조체에 대해서는이 경고를 생성 하지 않습니다 . 2.0에서는 사용되었지만 3.0 컴파일러에서는 사용되지 않았습니다. 코드는 여전히 제거되지만 (코드에 연결할 수 없음을 알 수 있음) 경고가 생성되지 않습니다.
using System;
struct MyValue
{
private readonly int value;
public MyValue(int value) { this.value = value; }
public static bool operator ==(MyValue x, MyValue y) {
return x.value == y.value;
}
public static bool operator !=(MyValue x, MyValue y) {
return x.value != y.value;
}
}
class Program
{
static void Main()
{
int i = 1;
MyValue v = new MyValue(1);
if (i == null) { Console.WriteLine("a"); } // warning
if (v == null) { Console.WriteLine("a"); } // no warning
}
}
IL (for Main
)- (부작용이있을 수있는)을 제외한 모든MyValue(1)
것이 제거되었습니다.
.method private hidebysig static void Main() cil managed
{
.entrypoint
.maxstack 2
.locals init (
[0] int32 i,
[1] valuetype MyValue v)
L_0000: ldc.i4.1
L_0001: stloc.0
L_0002: ldloca.s v
L_0004: ldc.i4.1
L_0005: call instance void MyValue::.ctor(int32)
L_000a: ret
}
이것은 기본적으로 :
private static void Main()
{
MyValue v = new MyValue(1);
}
비교가 결코 사실 일 수 없다는 사실이 그것이 불법임을 의미하지는 않습니다. 그럼에도 불구하고 값 유형은 null
.
아니, Int32 x
지금까지이되지 않습니다 null
.
If you are comparing an int to null then the comparison operator that takes two int?s is applicable.
"Why a comparison of a value type with null is a warning?" article will help you.
A value type cannot be null
, although it could be equal to null
(consider Nullable<>
). In your case the int
variable and null
are implicitly cast to Nullable<Int32>
and compared.
I suspect that your particular test is just being optimized out by the compiler when it generates the IL since the test will never be false.
Side Note: It is possible to have a nullable Int32 use Int32? x instead.
I guess this is because "==" is a syntax sugar which actually represents call to System.Object.Equals
method that accepts System.Object
parameter. Null by ECMA specification is a special type which is of course derived from System.Object
.
That's why there's only a warning.
[EDITED: made warnings into errors, and made operators explicit about nullable rather than the string hack.]
As per @supercat's clever suggestion in a comment above, the following operator overloads allow you to generate an error about comparisons of your custom value type to null.
By implementing operators that compare to nullable versions of your type, the use of null in a comparison matches the nullable version of the operator , which lets you generate the error via the Obsolete attribute.
Until Microsoft gives us back our compiler warning I'm going with this workaround, thanks @supercat!
public struct Foo
{
private readonly int x;
public Foo(int x)
{
this.x = x;
}
public override string ToString()
{
return string.Format("Foo {{x={0}}}", x);
}
public override int GetHashCode()
{
return x.GetHashCode();
}
public override bool Equals(Object obj)
{
return x.Equals(obj);
}
public static bool operator ==(Foo a, Foo b)
{
return a.x == b.x;
}
public static bool operator !=(Foo a, Foo b)
{
return a.x != b.x;
}
[Obsolete("The result of the expression is always 'false' since a value of type 'Foo' is never equal to 'null'", true)]
public static bool operator ==(Foo a, Foo? b)
{
return false;
}
[Obsolete("The result of the expression is always 'true' since a value of type 'Foo' is never equal to 'null'", true)]
public static bool operator !=(Foo a, Foo? b)
{
return true;
}
[Obsolete("The result of the expression is always 'false' since a value of type 'Foo' is never equal to 'null'", true)]
public static bool operator ==(Foo? a, Foo b)
{
return false;
}
[Obsolete("The result of the expression is always 'true' since a value of type 'Foo' is never equal to 'null'", true)]
public static bool operator !=(Foo? a, Foo b)
{
return true;
}
}
I think the best answer as to why the compiler accepts this is for generic classes. Consider the following class...
public class NullTester<T>
{
public bool IsNull(T value)
{
return (value == null);
}
}
If the compiler didn't accept comparisons against null
for value types, then it would essentially break this class, having an implicit constraint attached to its type parameter (i.e. it would only work with non-value-based types).
The compiler will allow you to compare any struct implementing the ==
to null. It even allows you to compare an int to null (you would get a warning though).
But if you disassemble the code you will see that the comparison is being solved when the code is compiled. So, for instance, this code (where Foo
is a struct implementing ==
):
public static void Main()
{
Console.WriteLine(new Foo() == new Foo());
Console.WriteLine(new Foo() == null);
Console.WriteLine(5 == null);
Console.WriteLine(new Foo() != null);
}
Generates this IL:
.method public hidebysig static void Main() cil managed
{
.entrypoint
// Code size 45 (0x2d)
.maxstack 2
.locals init ([0] valuetype test3.Program/Foo V_0)
IL_0000: nop
IL_0001: ldloca.s V_0
IL_0003: initobj test3.Program/Foo
IL_0009: ldloc.0
IL_000a: ldloca.s V_0
IL_000c: initobj test3.Program/Foo
IL_0012: ldloc.0
IL_0013: call bool test3.Program/Foo::op_Equality(valuetype test3.Program/Foo,
valuetype test3.Program/Foo)
IL_0018: call void [mscorlib]System.Console::WriteLine(bool)
IL_001d: nop
IL_001e: ldc.i4.0
IL_001f: call void [mscorlib]System.Console::WriteLine(bool)
IL_0024: nop
IL_0025: ldc.i4.1
IL_0026: call void [mscorlib]System.Console::WriteLine(bool)
IL_002b: nop
IL_002c: ret
} // end of method Program::Main
As you can see:
Console.WriteLine(new Foo() == new Foo());
Is translated to:
IL_0013: call bool test3.Program/Foo::op_Equality(valuetype test3.Program/Foo,
valuetype test3.Program/Foo)
Whereas:
Console.WriteLine(new Foo() == null);
Is translated to false:
IL_001e: ldc.i4.0
참고URL : https://stackoverflow.com/questions/1972262/c-sharp-okay-with-comparing-value-types-to-null
'IT박스' 카테고리의 다른 글
Eclipse에서 문자를 새 줄로 바꾸려면 어떻게합니까? (0) | 2020.09.19 |
---|---|
MySQL에서 STRAIGHT_JOIN을 사용하는 경우 (0) | 2020.09.19 |
Android XML 레이아웃의 '포함'태그가 실제로 작동합니까? (0) | 2020.09.19 |
서블릿과 웹 서비스의 차이점 (0) | 2020.09.19 |
콜백 Ajax JSON을 사용한 jQuery 자동 완성 (0) | 2020.09.19 |