2013-11-15 1 views
10

구조체 컬렉션에서 FirstOrDefault()를 사용한 후 이상한 동작이 나타납니다. 나는 그것을이 재현 사례로 분리했다. 이 프로그램은 컴파일러 오류가 아니라 비밀입니다왜 연산자 '=='구조체 및 기본 (구조체) 적용 할 수 없습니다?

using System; 
using System.Linq; 

namespace MyProgram { 
    public class Program { 
     static void Main() { 

      var users = new User[] { 
      new User() { UserGuid = Guid.NewGuid(), Username = "user01" }, 
      new User() { UserGuid = Guid.NewGuid(), Username = "user02" } 
     }; 

      var user = users.FirstOrDefault(u => u.Username == "user01"); 
      Console.WriteLine(user == default(User) ? "not found" : "found"); 
     } 

    } 

    public struct User { 
     public Guid UserGuid; 
     public string Username; 
    } 
} 

컴파일되지 않습니다 :

유형 'MyProgram.User'와 'MyProgram.User'의 피연산자에 적용 할 수 없습니다 연산자 '=='

구조체를 클래스로 변경하면 문제가 없지만 기본적으로 구조체 '인스턴스'를 비교할 수없는 이유는 무엇입니까? 당신이 ==을 무시할 경우이

public static bool operator ==(User u1, User u2) 
    { 
     return u1.Equals(u2) 
    } 

또한 또한 EqualsGetHashCode()

를 오버라이드 (override) 할 필요가 수행하려는 경우

+1

클래스의 경우 포인터 비교를 수행하기 때문에. 구조체에 대해서는 정의되지 않았다. (구현 자에 의해 정의되지 않았다면 확장 된 반사 비교를해야하기 때문이다.) 구조체에 클래스가 포함되어 있는지 생각해보십시오 (가능한 경우). 이 경우 비교를 수행해야합니다 (각 클래스에 대해 비교 연산자를 호출하여 참조 만하면됩니다). 구조체에 큰 데이터 배열이 포함되어 있다면 생각해보십시오 (좋지 않은 나쁜 습관이지만 허용되고 책임감 있음). 어떤 컴파일러가이를 방출해야합니까? 그 이유로 ** IMO는 Equals()를 호출조차도 꽤 비효율적입니다 **. –

+1

가능한 중복 [==]를 사용하여 두 구조체 비교 (0120-99026/comparing-two-structs-using) – Stijn

+0

어쨌든 클래스 대신 구조체를 사용하고 있습니까? (나는 당신의 결정에 의문을 제기하지 않고 단지 그것에 대해 생각하고 그 차이를 이해하는지 궁금해합니다.) –

답변

13

클래스의 경우 == 연산자는 참조 항등을 사용합니다. 물론 구조체는 값 유형이므로 참조로 비교할 수 없습니다. 형식에 따라 멤버 별 비교가 항상 유효한 비교가 아니기 때문에 구조체에 대한 기본 구현은 ==입니다.

Console.WriteLine(user.Equals(default(User)) ? "not found" : "found"); 

아니면 단지 ==을 구현할 수 Object.Equals 전화 :

대신 memberwise 비교 않는 Object.Equals 방법을 사용할 수 있습니다 그러나

public static bool operator ==(User lhs, User rhs) 
{ 
    return lhs.Equals(rhs); 
} 

, 구조체에 대한 Equals의 기본 구현을 반사를 사용하므로 매우 느립니다. (너무 가능성이 GetHashCode) ==!=과 함께 Equals을 직접 구현하기 위해 더 나은 것 :

C#에서
public override bool Equals(Object obj) 
{ 
    return obj is User && Equals((User)obj); 
} 

public bool Equals(User other) 
{ 
    return UserGuid == other.UserGuid && Username == other.Username; 
} 

public static bool operator ==(User lhs, User rhs) 
{ 
    return lhs.Equals(rhs); 
} 

public static bool operator !=(User lhs, User rhs) 
{ 
    return !lhs.Equals(rhs); 
} 
+0

당신이 말한 것을 덧붙이면'Equals'를 오버라이드하면'GetHashCode'를 오버라이드해야한다는 컴파일러 경고를 받게됩니다. –

+0

그러나 int는 값 형식이기도하며 두 개의 int를 비교하기 위해 여전히 ==를 사용합니다. – rajibdotnet

0

당신은 당신이 아마로 !=를 오버라이드 (override) 할 것이라고 == 연산자를 오버로드 할 수 있습니다 잘.

public static bool operator !=(User u1, User u2) 
    { 
     return !u1.Equals(u2) 
    } 
2

당신은 단지 그것을 구현해야한다 :

public static bool operator == (User u1, User u2) 
{ 
    return u1.Equals(u2); // use ValueType.Equals() which compares field-by-field. 
} 
+0

또한'! ='을 구현해야합니다. https://stackoverflow.com/a/15199135/695964 – KFL

0

두 참조 유형을 비교하면 참조가 동일한 유형을 가리 여부를 확인하고 있습니다.

하지만 값 유형을 다루는 경우 비교할 참조가 없습니다.

연산자를 직접 구현해야하며 값 유형의 필드가 일치하는지 (아마) 확인해야합니다.

1

== 토큰이 두 개의 서로 다른 연산자 (모든 언어에 대해 같은 토큰을 사용 나타내는 데 사용됩니다 VB.NET은 토큰 =Is을 사용합니다.연산자 중 하나는 오버로드 가능한 동등성 테스트이며 두 피연산자 유형에 대해 오버로드가 정의되거나 하나의 피연산자 유형에 대해 오버로드가 정의되고 다른 피연산자가 암시 적으로 변환 가능한 유형에서만 사용할 수 있습니다. 다른 연산자는 참조 동등성 테스트를 나타내며 동등 테스트 연산자가 사용 불가능한 경우에 사용할 수 있으며 한 피연산자가 다른 피연산자에서 파생되는 클래스 유형 인 경우 한 피연산자는 클래스 유형이고 다른 피연산자는 인터페이스 유형 또는 두 피연산자 모두 인터페이스 유형입니다.

첫 번째 동등 검정 연산자는 명시 적 재정의를 제공하지 않는 모든 유형 (클래스, 인터페이스 또는 구조체)과 함께 사용할 수 없습니다. 그러나 첫 번째 동등 검정 연산자를 사용할 수없는 경우 == 토큰이 사용되는 경우 C#은 두 번째 연산자를 사용하려고 시도합니다. [VB.NET과 같은 다른 언어도이 작업을 수행하지 않습니다. VB.NET에서 =을 사용하여 동등성 테스트 오버로드를 정의하지 않는 두 가지를 비교하려는 시도는 Is 연산자를 사용하여 비교할 수있는 경우에도 오류가됩니다. 두 번째 연산자는 모든 참조 유형을 동일한 유형의 다른 참조와 비교하는 데 사용할 수 있지만 구조에는 사용할 수 없습니다. 구조체에 대해 동일한 유형의 항등 연산자가 정의되어 있지 않으므로 비교가 허용되지 않습니다.

==은 단순히 모든 유형에 사용할 수 Equals(Object),에 후퇴하지 않는 이유 하나가 궁금해하는 경우는, 그 이유는 ==의 두 피연산자가 Equals 일치에서의 동작을 방해하는 방법으로 강제 입력 될 수 있다는 것입니다. 예를 들어, 1.0f == 1.0, 1.0 == 1.0f의 경우, 모두 피연산자를 double으로 캐스트하지만 (1.0f).Equals(1.0)과 같은 표현식을 사용하면 첫 번째 피연산자는 float 이외의 것으로 평가 될 수 없습니다. 또한 ==Equals에 매핑 된 경우 C#에서 다른 토큰을 사용하여 참조 동등성 테스트를 표현해야합니다. [언어가 어쨌든 완료해야하지만 분명히 수행하기를 원하지 않는 것입니다.]