C # 7에서는 사용할 수 있습니다
if (x is null) return;
대신에
if (x == null) return;
기존 방식보다 새로운 방식 (이전 예)을 사용하면 어떤 이점이 있습니까?
시맨틱이 다른가?
맛의 문제 일까? 그렇지 않다면 언제 다른 것을 사용해야합니까?
참조 : C # 7.0의 새로운 기능 .
답변
업데이트 : 오버로드 된 항등 연산자가 없을 때 Roslyn 컴파일러가 두 연산자의 동작을 동일하게 만들도록 업데이트되었습니다 . 참조하십시오 현재 컴파일러 결과에 코드를 ( M1
그리고 M2
코드에서) 그 어떠한 오버로드 같음 비교가 없을 때 어떻게되는지 보여줍니다. 둘 다 이제 성능이 더 좋습니다 ==
. 오버로드 된 동등 비교기가있는 경우 코드는 여전히 다릅니다 .
이전 버전의 Roslyn 컴파일러는 아래 분석을 참조하십시오.
들어 null
우리는 C #과에 사용되는 것과 차이가없는 변경할 때 6. 그러나, 일이 재미가 될 null
또 다른 상수.
예를 들면 다음과 같습니다.
Test(1);
public void Test(object o)
{
if (o is 1) Console.WriteLine("a");
else Console.WriteLine("b");
}
테스트 결과는 다음과 같습니다 a
. o == (object)1
당신이 그것을 평소에 쓴 것과 비교 하면, 그것은 큰 차이를 만듭니다. is
비교의 다른 측면에서 유형을 고려합니다. 그것은 멋지다!
나는 생각 == null
대에 is null
일정한 패턴이의 구문, ‘실수로’잘 알고 그냥 뭔가 is
연산자와이 같은 결과 운영자 수율 같습니다.
svick이 언급 했듯이 , is null
calls System.Object::Equals(object, object)
where ==
callsceq
.
에 대한 IL is
:
IL_0000: ldarg.1 // Load argument 1 onto the stack
IL_0001: ldnull // Push a null reference on the stack
IL_0002: call bool [mscorlib]System.Object::Equals(object, object) // Call method indicated on the stack with arguments
IL_0007: ret // Return from method, possibly with a value
에 대한 IL ==
:
IL_0000: ldarg.1 // Load argument 1 onto the stack
IL_0001: ldnull // Push a null reference on the stack
IL_0002: ceq // Push 1 (of type int32) if value1 equals value2, else push 0
IL_0004: ret // Return from method, possibly with a value
우리가 이야기하고 있기 때문에 null
, 이것은 인스턴스에 차이를주기 때문에 차이는 없습니다 . 항등 연산자에 과부하가 걸리면 변경 될 수 있습니다.
답변
과부하 등가 연산자
실제로 연산자에 null
과부하가 걸린 유형과 비교할 때 두 비교 사이에는 시맨틱에 차이가 있습니다 ==
. foo is null
직접 참조 비교를 사용하여 결과를 결정하는 반면 foo == null
, 과부하 된 ==
연산자가 존재하는 경우 물론 실행 합니다.
이 예제에서는 오버로드 된 ==
연산자 에 “버그”를 도입 하여 두 번째 인수가 null
다음과 같은 경우 항상 예외를 발생시킵니다 .
void Main()
{
Foo foo = null;
if (foo is null) Console.WriteLine("foo is null"); // This condition is met
if (foo == null) Console.WriteLine("foo == null"); // This will throw an exception
}
public class Foo
{
public static bool operator ==(Foo foo1, Foo foo2)
{
if (object.Equals(foo2, null)) throw new Exception("oops");
return object.Equals(foo1, foo2);
}
// ...
}
IL 코드 foo is null
는 ceq
명령어를 사용하여 직접 참조 비교를 수행합니다.
IL_0003: ldloc.0 // foo
IL_0004: ldnull
IL_0005: ceq
IL 코드 foo == null
는 오버로드 된 연산자에 대한 호출을 사용합니다.
IL_0016: ldloc.0 // foo
IL_0017: ldnull
IL_0018: call UserQuery+Foo.op_Equality
따라서 ==
사용자 코드 를 사용 하면 사용자 코드가 실행 되지 않을 수 있습니다 (예기치 않은 동작 또는 성능 문제가 발생할 수 있음).
제네릭에 대한 제한
is null
구문을 사용하면 형식이 참조 형식으로 제한됩니다. 컴파일러는이를 보장 is null
하므로 값 유형에 사용할 수 없습니다 . 제네릭 메서드가있는 경우 is null
제네릭 형식이 참조 형식으로 제한되지 않으면 사용할 수 없습니다 .
bool IsNull<T>(T item) => item is null; // Compile error: CS0403
bool IsNull<T>(T item) => item == null; // Works
bool IsNull<T>(T item) where T : class => item is null; // Works
이것을 지적 해 주신 David Augusto Villa 에게 감사드립니다 .