2016-06-01 2 views
10

나는 설명 할 수없는 매우 이상한 것을보고 있습니다. 내가 익숙하지 않은 C# 또는 런타임/이미 터의 버그를 예상하고있다. - 내가 내 DB에 존재하지 않는 알고 개체에 대한 true을 반환 내 응용 프로그램을 테스트하는 동안null과의 비교는 expr == null과 expr! = null 모두에 대해 true로 평가됩니다.

public static bool HistoryMessageExists(DBContext context, string id) 
{ 
    return null != context.GetObject<HistoryMessage>(id); 
} 

, 나는이 오작동을 참조하십시오

나는 다음과 같은 방법이있다. 그래서 나는 방법에 멈춰 섰고, 즉시에, 나는 다음 실행 :

context.GetObject<HistoryMessage>(id) 
null 
null == context.GetObject<HistoryMessage>(id) 
true 
null != context.GetObject<HistoryMessage>(id) 
true 

GetObject은과 같이 정의된다 : object에 식을 캐스팅 할 때

public T GetObject<T>(object pk) where T : DBObject, new() 
{ 
    T rv = Connection.Get<T>(pk); 

    if (rv != null) 
    { 
     rv.AttachToContext(this); 
     rv.IsInserted = true; 
    } 

    return rv; 
} 

는 흥미롭게도, 비교가 제대로 평가를 :

null == (object)context.GetObject<HistoryMessage>(id) 
true 
null != (object)context.GetObject<HistoryMessage>(id) 
false 

동일성 연산자가 우선 적용되지 않습니다.

편집 : 연산자 오버로드가 잘못된 것으로 밝혀졌습니다. 그런데 내부 메소드 GetObject에서 동등성이 올바르게 평가되는 이유는 rv이이 경우 HistoryMessage 인 경우입니다.

public class HistoryMessage : EquatableIdentifiableObject 
{ 
    public static bool HistoryMessageExists(DBContext context, string id) 
    { 
     var rv = context.GetObject<HistoryMessage>(id); 
     bool b = rv != null; 
     return b; 
    } 

    public static void AddHistoryMessage(DBContext context, string id) 
    { 
     context.InsertObject(new HistoryMessage { Id = id }); 
    } 
} 

public abstract partial class EquatableIdentifiableObject : DBObject, IObservableObject 
{ 
    public event PropertyChangedEventHandler PropertyChanged; 

    [PrimaryKey] 
    public string Id { get; set; } 

    //... 
} 

public abstract partial class EquatableIdentifiableObject 
{ 
    //... 

    public static bool operator ==(EquatableIdentifiableObject self, EquatableIdentifiableObject other) 
    { 
     if (ReferenceEquals(self, null)) 
     { 
      return ReferenceEquals(other, null); 
     } 

     return self.Equals(other); 
    } 

    public static bool operator !=(EquatableIdentifiableObject self, EquatableIdentifiableObject other) 
    { 
     if (ReferenceEquals(self, null)) 
     { 
      return !ReferenceEquals(other, null); 
     } 

     return !self.Equals(other); 
    } 
} 

public abstract class DBObject 
{ 
    [Ignore] 
    protected DBContext Context { get; set; } 

    [Ignore] 
    internal bool IsInserted { get; set; } 

    //... 
} 

여기 무슨 일이 일어나나요?

+2

'HistoryMessage'가 항등 연산자를 구현합니까? –

+0

@ LasseV.Karlsen 아니요, 무시하지 마세요. 여기에 운영자가 무시되지 않는다는 질문을 쓰는 것을 잊어 버렸습니다. –

+1

'HistoryMessage'의 기본 클래스 중 하나에조차 없습니까? –

답변

3
  • 과부하가 잘못되어 있기 때문에 == 연산자가 입력에 실패했습니다.
  • object's 구현이 ==이고 EquatableIdentifiableObject's이 아니기 때문에 개체에 캐스팅 할 때 == 연산자가 올바르게 작동했습니다.
  • GetObject 메서드에서 사용중인 ==EquatableIdentifiableObject's 구현이 아니므로 연산자가 올바르게 평가됩니다. C#에서 제네릭은 컴파일 타임이 아니라 런타임시 (적어도 관련성이있는 의미에서) 해결됩니다. ==은 정적이며 가상이 아닙니다. 따라서 유형 T은 런타임에 해결되지만 컴파일시에는 ==에 대한 호출을 해결해야합니다. 컴파일시 컴파일러가 ==을 해결할 때 의 EquatableIdentifiableObject's 구현을 사용하지 않을 것입니다. 유형 T에는 where T : DBObject, new(), DBObject's 구현 (있는 경우)이 사용됩니다. DBObject==을 정의하지 않는 경우 (최대 object까지) 첫 번째 기본 클래스의 구현이 사용됩니다.==EquatableIdentifiableObject's 구현에 대한 자세한

몇 가지 의견 :

if (ReferenceEquals(self, null)) 
{ 
    return ReferenceEquals(other, null); 
} 

과 :

당신은이 부분을 대체 할 수
  • public static bool operator !=(EquatableIdentifiableObject self, EquatableIdentifiableObject other) 
    { 
        return !(self == other); 
    } 
    
    • 당신이 ==의 서명을 정의하는 방법은 약간 오해의 소지가 :
      • 이와
      public static bool operator !=(EquatableIdentifiableObject self, EquatableIdentifiableObject other) 
      { 
          ... 
      } 
      

      대체하는보다 강력한 것입니다. 첫 번째 매개 변수의 이름은 self이고 두 번째 매개 변수의 이름은 other입니다. 만약 ==이 인스턴스 메소드라면 괜찮을 것입니다. 정적 메서드이기 때문에 self이라는 이름은 약간 오도 된 것입니다. 더 나은 이름은 o1o2이거나이 행에있는 것이므로 두 피연산자가 더 동등한 위치에서 처리됩니다.

+1

설명해 주셔서 감사합니다. 나는 그들이 정적이기 때문에 사업자가 가상이 아니라고 추측하고있다. 연산자가 정적으로 구현되는 이유는 무엇입니까? 널 에지 경우 때문입니까? –

+0

다음을 참조하십시오 : http://stackoverflow.com/questions/2018108/why-must-c-sharp-operator-overloads-be-static – Ladi

+0

BTW, 자신 만의 일반적인 방법을 쓰고 가상 측면의 이점을 원한다면 가상 '평등'을 덮어 쓰고이를 호출하여 비교할 수 있습니다. – Ladi

1

지금 알다시피 operator ==(...)의 여러 오버로드가있을 수 있습니다. 그 중 일부는 C# 기본 제공 오버로드가 될 수 있고 다른 일부는 사용자 정의 연산자 일 수 있습니다.

Visual Studio에서 != 또는 == 기호 위로 마우스를 가져 가면 과부하 해결로 선택한 과부하가 표시됩니다 (VS2013까지 선택한 과부하가 실제로 사용자 정의 된 경우에만 표시됨). , VS2015에서는 내가 믿는 모든 경우에 그것을 보여줄 것입니다).

== (즉 호출되는 과부하) 컴파일시에 정적으로 결합 고정된다. 그것에 대해 아무것도 동적 또는 가상입니다. 당신이 그렇다면 :

public T SomeMethod<T>() where T : SomeBaseClass 
{ 
    T rv = ...; 

    if (rv != null) 
    { 

다음 (==에 대한 몇 가지 특별한 규칙 포함) 보통 과부하 해상도, 컴파일시에 고정됩니다 사용 !=의 과부하가있다. rv은 유형이 T이고 이는 SomeBaseClass과 동일하거나 그에 기반한 참조 유형으로 알려져 있습니다. 따라서 최상의 과부하는이를 기반으로 선택됩니다. SomeBaseClass이 적절한 과부하를 정의하지 않거나 "상속"하지 않는 경우 operator !=(object, object) 과부하 (내장) 일 수 있습니다.

T에 대한 실제 대체가 더 구체적인 유형 SomeEqualityOverloadingClass (물론 제약 조건을 충족 함) 일지라도 실행시에 새로운 과부하 해결이 발생한다는 것을 의미하지는 않습니다!

이것은 virtual 방법 .Equals(object)과 다릅니다.

C#에서 제네릭은 템플릿처럼 작동하지 않으며 dynamic과 다릅니다.

당신이 정말로 dynamic 오버로드 확인 (이 컴파일시 런타임에 대신에 바인딩)합니다

, if ((dynamic)rv != null) 말을 할 수있다.

관련 문제