[C#] “x는 null”과“x == null”의 차이점은 무엇입니까?

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 nullcalls 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 nullceq명령어를 사용하여 직접 참조 비교를 수행합니다.

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 에게 감사드립니다 .


답변