C #에서 프리미티브의 ==와 Equals ()의 차이점은 무엇입니까?


180

이 코드를 고려하십시오.

int age = 25;
short newAge = 25;
Console.WriteLine(age == newAge);  //true
Console.WriteLine(newAge.Equals(age)); //false
Console.ReadLine();

intshort기본 유형이지만 둘 과 의 비교 ==는 true 를 리턴하고 비교 Equals는 false 를 리턴합니다.

왜?


9
@OrangeDog 질문에 대해 생각하고 투표 종료

4
이것은 명백한 리버스 시도가 빠져있다 :Console.WriteLine(age.Equals(newAge));
ANeves

3
중복은이 동작을 설명하지 않습니다. 그것은 Equals()일반적으로 무엇에 관한 것 입니다.
SLaks

37
며칠 전에 Coverity 블로그에서이 정확한 질문에 답변했습니다. blog.coverity.com/2014/01/13/inconsistent-equality
Eric Lippert

5
@CodesInChaos : 명세서는 실제로 "기본형"이라는 용어를 정의하지 않고 두 번 사용합니다. 이는 기본 유형이 내장 된 값 유형이라는 것을 의미하지만, 이는 명확하지 않습니다. 나는 Mads에게 용어가 제거하는 것보다 더 혼란스러워하는 것처럼 보이기 때문에 단순히 사양에서 벗어나는 것이 좋습니다.
Eric Lippert

답변:


262

짧은 답변:

평등은 복잡합니다.

자세한 답변 :

기본 요소 유형은 기본을 대체하고 object.Equals(object)상자 object가 동일한 유형 및 값인 경우 true를 리턴합니다 . (널링 가능 유형에서도 작동합니다. 널이 아닌 널 입력 가능 유형은 항상 기본 유형의 인스턴스에 적용됩니다.)

newAgeis 이므로 동일한 값으로 boxed short 를 전달하면 short해당 Equals(object)메소드 만 true를 리턴합니다 . boxed를 전달 하므로 false를 반환합니다.int

대조적으로, ==연산자는 2 int초 (또는 1 short초) 를 취하는 것으로 정의됩니다 long.
당신이 그것을 호출 할 때 intshort, 컴파일러는 암시 적으로 변환됩니다 shortint그 결과 비교 int값으로들.

그것을 작동시키는 다른 방법들

기본 유형 Equals()에는 동일한 유형을 허용하는 자체 메소드 도 있습니다 .
작성 age.Equals(newAge)하면 컴파일러 int.Equals(int)에서 최상의 과부하를 선택 하고 암시 적으로로 변환 short합니다 int. true이 메소드는 단순히 ints를 직접 비교하기 때문에을 리턴 합니다.

short또한 short.Equals(short)메소드가 있지만 int암시 적으로로 변환 short할 수 없으므로 호출하지 않습니다.

캐스트 로이 메소드를 강제로 호출 할 수 있습니다.

Console.WriteLine(newAge.Equals((short)age)); // true

이것은 short.Equals(short)권투없이 직접 호출 합니다. 경우 age32767보다 큰, 그것은 오버 플로우 예외가 발생합니다.

short.Equals(object)오버로드를 호출 할 수도 있지만 박스형 객체를 명시 적으로 전달하여 동일한 유형을 얻습니다.

Console.WriteLine(newAge.Equals((object)(short)age)); // true

이전 대안과 마찬가지로,에 맞지 않으면 오버플로가 발생합니다 short. 이전 솔루션과 달리 short시간과 메모리를 낭비하여 객체를 상자에 넣습니다.

소스 코드:

Equals()실제 소스 코드의 두 가지 방법은 다음과 같습니다 .

    public override bool Equals(Object obj) {
        if (!(obj is Int16)) {
            return false;
        }
        return m_value == ((Int16)obj).m_value;
    }

    public bool Equals(Int16 obj)
    {
        return m_value == obj;
    }

추가 자료 :

Eric Lippert를 참조하십시오 .


3
@SLaks, 호출 long == int하면 int암시 적으로 long오른쪽으로 변환 됩니까?
Selman Genç

1
그리고 네, 실제로 시도하지 않고 모든 것을 작성했습니다.
SLaks

1
하나 명의 변경이있는 경우, 질문의 코드에서 그 기억 int age = 25;const int age = 25;, 그 결과가 변경됩니다. 에서 암시 적 변환 때문이다 int에이 short경우에 존재 않습니다. 암시 적 상수 표현식 변환을 참조하십시오 .
Jeppe Stig Nielsen

2
@SLaks 예. 그러나 "전달 된 값"이라는 답은 개발자가 전달한 값 또는 개봉 후 CLR이 실제로 전달한 값으로 두 가지 방식으로 해석 될 수 있습니다. 난 이미 여기에 답을 모르는 일반 사용자가 전자로 읽을 것 같은데요
JaredPar

2
@Rachel : 사실이 아니라면; 기본 == 조작은 참고 문헌으로 참조 형식을 비교한다. 값 유형과 과부하 유형의 ==경우 그렇지 않습니다.
SLaks

55

이 때문에 대한 과부하 short.Equals그은을지지 않습니다 int. 따라서 이것을 다음이라고합니다.

public override bool Equals(object obj)
{
    return obj is short && this == (short)obj;
}

obj하지 않은 것이다 short.. 따라서,이 거짓.


12

Equals에 전달하면 다음과 같이 전달 int됩니다 .shortobject

여기에 이미지 설명을 입력하십시오 따라서이 의사 코드는 다음과 같이 실행됩니다.

return obj is short && this == (short)obj;


10

==동일한 조건을 확인하는 데 사용되며 연산자 (부울 연산자)로 간주 될 수 있습니다. 단지 2 가지를 비교하기 위해 여기에서 유형 캐스팅이 수행되므로 데이터 유형은 중요하지 않으며 Equals동등 조건을 확인하는 데에도 사용됩니다 그러나이 경우 데이터 유형이 동일해야합니다. N 같음은 연산자가 아닌 방법입니다.

아래는 귀하가 제공 한 것에서 가져온 작은 예이며 차이점을 간략하게 설명합니다.

int x=1;
short y=1;
x==y;//true
y.Equals(x);//false

위의 예에서 X와 Y는 같은 값, 즉 1을 사용 ==하며, 를 사용할 때 ==컴파일러가 short 유형을 int로 변환하여 결과를 제공하면 true를 반환합니다 .

우리가 사용할 때 Equals비교는 수행되지만 형식 캐스팅은 컴파일러에 의해 수행되지 않으므로 false가 반환됩니다.

여러분, 제가 틀렸다면 알려주십시오.


6

메소드 또는 연산자 인수가 필수 유형이 아닌 많은 컨텍스트에서 C # 컴파일러는 암시 적 유형 변환을 수행하려고 시도합니다. 컴파일러가 암시 적 변환을 추가하여 모든 인수가 연산자와 메서드를 만족시킬 수 있다면 경우에 따라 (특히 평등 테스트의 경우) 결과가 놀라 울 수도 있지만 불만없이 처리 할 수 ​​있습니다.

또한, 각각의 값 유형 은 값의 종류와 객체의 종류 (*)를 설명 int하거나 short실제로 설명합니다. 값을 다른 종류의 값으로 변환하고 모든 종류의 값을 해당하는 종류의 개체로 변환하기위한 암시 적 변환이 있지만 서로 다른 종류의 개체는 서로 암시 적으로 변환 할 수 없습니다.

하나는 사용하는 경우 ==비교 연산자 short와을 int의이 short암시 적으로 변환됩니다 int. 그 수치의 그 동일이라면 int1, int이 동등한 것이다 전환시켰다되는 int이 비교되는이. 그러나 Equals짧은 방법 을 사용 하여을 비교 하려고 시도하는 경우 메서드 int의 과부하를 충족시키는 암시 적 변환은에 Equals해당하는 객체 유형으로 변환하는 것 int입니다. short전달 된 객체와 일치하는지 여부를 묻는 메시지가 표시 되면 해당 객체 int가 a 가 아니라 관찰되어 short동일하지 않을 수 있다는 결론을 내립니다.

일반적으로 컴파일러는 그것에 대해 불평하지 않지만 같은 유형이 아닌 것을 비교하지 않아야합니다. 사물을 공통 형식으로 변환하는 것이 동일한 결과를 낼 것인지에 관심이 있다면, 그러한 변환을 명시 적으로 수행해야합니다. 예를 들어,

int i = 16777217;
float f = 16777216.0f;

Console.WriteLine("{0}", i==f);

inta와 a 를 비교할 수있는 세 가지 방법이 있습니다 float. 알고 싶은 사람 :

  1. 가장 가까운 float값이 int일치 float합니까?
  2. float일치하는 정수 부분이 int?
  3. 를 수행 int하고 float동일한 수치를 나타냅니다.

하나는 비교하려고하면 intfloat직접 컴파일 된 코드는 첫 번째 질문에 대답 할 것이다; 그러나 이것이 프로그래머가 의도 한 것과는 거리가 멀다. 비교를 변경하면 (float)i == f첫 번째 의미가 의도되었다는 것이 분명해 지거나 (double)i == (double)f코드가 세 번째 질문에 대답하게 될 것입니다 (그리고 그것이 의도 한 것임을 분명히합니다).

(*) C # 사양이 유형의 값을 예 System.Int32를 들어 유형 의 객체로 간주하더라도 System.Int32, 이러한 견해는 값과 객체를 다른 우주에 거주하는 것으로 간주하는 플랫폼에서 코드를 실행해야한다는 요구 사항과 모순됩니다. 또한,이 경우 T참조 형식이며, xA는 T다음 유형의 참조 T를 참조 할 수 있어야한다 x. 따라서 v유형 의 변수 Int32가을 보유하는 경우 유형 Object의 참조는 해당 내용에 Object대한 참조를 보유 할 수 있어야합니다 v. 실제로 형식의 참조는 Object에서 복사 된 데이터를 보유 v하고 있지만 v자체 또는 내용 을 가리키는 개체를 가리킬 수 있습니다 . 그것은 제안하지 않을 것입니다v그것의 내용은 실제로입니다 Object.


1
the only implicit conversion which would satisfy an overload of the Equals method would be the conversion to the object type corresponding to int잘못된. Java와 달리 C #에는 별도의 기본 및 상자 유형이 없습니다. 그것이 object유일한 다른 과부하이기 때문에 박스에 담겨 Equals()있습니다.
SLaks

첫 번째 질문과 세 번째 질문은 동일합니다. 로 변환하면 정확한 값이 이미 손실되었습니다 float. 에 a float를 캐스팅하면 double마법처럼 새로운 정밀함이 만들어지지 않습니다.
SLaks

@SLaks : C #이 실행되는 가상 머신을 설명하는 ECMA 사양에 따라 각 값 유형 정의는 두 가지 고유 한 유형을 만듭니다. C # 사양은 유형의 저장 위치와 유형 List<String>.Enumerator의 힙 객체의 내용이 동일하다고 말할 수 List<String>.Enumerator있지만 ECMA / CLI 사양은 서로 다르며 C #에서 사용될 때도 다르게 동작한다고 말합니다.
supercat

@SLaks : 비교 전에 if로 각각 변환 된 경우 double16777217.0 및 16777216.0이 생성되며 이는 같지 않습니다. 변환 i float하면 16과 같지만 16777216.0f가됩니다 f.
supercat

@SLaks : 저장소 위치 유형과 박스형 객체 유형의 차이점에 대한 간단한 예를 보려면이 방법을 고려하십시오 bool SelfSame<T>(T p) { return Object.ReferenceEquals((Object)p,(Object)p);}. 값 유형에 대응하는 박스형 객체 유형 ReferenceEquals아이덴티티 보존 업 캐스트 를 통한 파라미터 유형을 만족시킬 수 있고 ; 그러나 저장 위치 유형은 ID를 유지하지 않는 변환이 필요합니다 . 를 캐스팅 하면 원래 이외의 것에 대한 참조 TU생성 T되면 a T가 실제로는 아닌 것이 좋습니다 U.
supercat

5

같음 () 의 방법 으로 System.Object 클래스
구문 : 공공 가상 부울 같음 ()
추천 우리는 두 객체의 상태는 우리가 사용해야 비교하려는 경우 ) (같음 방법

위에서 언급했듯이 답변 == 연산자는 값이 동일 하다는 것을 비교합니다.

ReferenceEqual과 혼동하지 마십시오

Reference Equals ()
구문 : public static bool ReferenceEquals ()
지정된 객체 인스턴스가 동일한 인스턴스인지 확인합니다.


8
이것은 전혀 질문에 대답하지 않습니다.
SLaks

SLaks i dnt는 위의 질문에 대한 기본 예제로 설명되었습니다.
Sugat Mankar

4

당신이 알아야 ==할 것은 항상 메소드를 호출한다는 것입니다. 문제는 전화 ==하고 Equals같은 일을 전화 / 일을하는 것입니다.

참조 유형을 사용 ==하면 항상 참조가 동일한 지 여부를 먼저 확인합니다 ( Object.ReferenceEquals). Equals반면에 일부 값이 같은지 여부를 확인할 수 있습니다.

편집 : svick에 대답하고 SLaks 의견을 추가하려면 여기에 일부 IL 코드가 있습니다.

int i1 = 0x22; // ldc.i4.s ie pushes an int32 on the stack
int i2 = 0x33; // ldc.i4.s 
short s1 = 0x11; // ldc.i4.s (same as for int32)
short s2 = 0x22; // ldc.i4.s 

s1 == i1 // ceq
i1 == s1 // ceq
i1 == i2 // ceq
s1 == s2 // ceq
// no difference between int and short for those 4 cases,
// anyway the shorts are pushed as integers.

i1.Equals(i2) // calls System.Int32.Equals
s1.Equals(s2) // calls System.Int16.Equals
i1.Equals(s1) // calls System.Int32.Equals: s1 is considered as an integer
// - again it was pushed as such on the stack)
s1.Equals(i1) // boxes the int32 then calls System.Int16.Equals
// - int16 has 2 Equals methods: one for in16 and one for Object.
// Casting an int32 into an int16 is not safe, so the Object overload
// must be used instead.

그렇다면 두 int호출을 == 호출과 비교하는 방법은 무엇 입니까? 힌트 : 전혀 없습니다 operator ==위한 방법은 Int32,하지만 하나있다String .
svick

2
이 질문에 전혀 대답하지 않습니다.
SLaks

@ SLaks : 실제로 int 및 짧은 비교에 대한 특정 질문에 대답하지 못하므로 이미 대답했습니다. 나는 여전히 ==마술을하는 것이 아니라, 단순히 메소드를 호출 한다고 설명하는 것이 흥미 롭습니다. (대부분의 프로그래머는 아마도 어떤 연산자도 구현하거나 재정의하지 않았습니다.) 내 대답을 추가하는 대신 질문에 의견을 추가했을 수도 있습니다. 내가 말한 내용이 적절하다고 생각되면 언제든지 업데이트하십시오.
user276648

참고 ==프리미티브 타입에없는 연산자 과부하 있지만로 컴파일 극한 언어 기능이다 ceqIL 명령어.
SLaks

3

== 원시적

Console.WriteLine(age == newAge);          // true

원시 비교에서 == 연산자는 매우 명백하게 작동합니다. C #에는 많은 == 연산자 과부하가 있습니다.

  • 문자열 == 문자열
  • int == int
  • uint == uint
  • 긴 == 긴
  • 더 많은

따라서이 경우에는 거기에서 어떠한 암시 적 변환이 없습니다 int에는 short그러나 short에이 int가능하다. 따라서 newAge는 int로 변환되고 비교가 발생하여 둘 다 동일한 값을 보유하므로 true를 리턴합니다. 따라서 다음과 같습니다.

Console.WriteLine(age == (int)newAge);          // true

Primitive의 .Equals ()

Console.WriteLine(newAge.Equals(age));         //false

여기서 Equals () 메소드가 무엇인지 알아야합니다. 짧은 유형 변수로 Equals를 호출합니다. 따라서 세 가지 가능성이 있습니다.

  • 같음 (객체, 객체) // 객체의 정적 메소드
  • 같음 (객체) // 객체의 가상 메소드
  • 같음 (짧음) // IEquatable을 구현합니다. 같음 (짧음)

첫 번째 유형은 int 유형의 인수 하나만 사용하여 호출하는 인수 수가 다르므로 여기서는 그렇지 않습니다. 위에서 언급했듯이 int를 short로 암시 적으로 변환하는 것은 불가능합니다. 여기에 두 번째 유형이 Equals(object)있습니다. 는 short.Equals(object)것입니다 :

bool Equals(object z)
{
  return z is short && (short)z == this;
}

따라서 z is shortz가 int이므로 false를 반환하는 조건이 테스트되었습니다 .

Eric Lippert의 자세한 기사는 다음과 같습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.