2013-08-05 13 views
8

Mam klasę z dwoma przesłonięciami dla operatora ==, aby porównać ją z innymi instancjami tej klasy i porównać z instancją ciągu.Sprawdzanie wartości Null jest niejednoznaczne dla klasy z kilkoma przesłonięciami dla operatora ==

class SomeClass 
{ 
    string value; 
    public SomeClass (string _Value) 
    { 
     value = _Value; 
    } 

    static public bool operator == (SomeClass C1, SomeClass C2) 
    { 
     return C1.value == C2.value; 
    } 

    static public bool operator != (SomeClass C1, SomeClass C2) 
    { 
     return C1.value != C2.value; 
    } 

    static public bool operator == (SomeClass C1, string C2) 
    { 
     return C1.value == (string) C2; 
    } 

    static public bool operator != (SomeClass C1, string C2) 
    { 
     return C1.value != (string) C2; 
    } 
} 

Jednak, gdy staram się porównać tę klasę na null:

 Console.WriteLine(someObject == null); 

pojawia się następujący błąd:

Error CS0121: The call is ambiguous between the following methods or properties: `SomeClass.operator ==(SomeClass, SomeClass)' and `SomeClass.operator ==(SomeClass, string)' 

Jak należy definiować moje == zastępuje więc mogę nadal sprawdza wartości NULL tej klasy?

+1

Być może coś jest mądry można zrobić z [ null coalesce] (http://msdn.microsoft.com/en-us/library/ms173224.aspx) – tnw

Odpowiedz

6

Ponieważ używasz literału null, kompilator nie wie, którą metodę wywołać, ponieważ zarówno string, jak i SomeClass może mieć wartość null.

Jedną z technik wymuszających na kompilatorze wybór jednej z metod jest wpisanie wartości null.

Console.WriteLine(someObject == ((SomeClass)null)); 

Albo jeszcze lepiej, zamiast korzystania null jawnie, należy użyć słowa kluczowego default uzyskać wartość zerową (bo default(T) jest null, gdy T jest typem odniesienia).

Console.WriteLine(someObject == default(SomeClass)); 
+0

To okazało się najbardziej eleganckim sposobem, dzięki. Chociaż (SomeClass) null może wyglądać dziwnie w kodzie (z pewnością byłbym tym zaskoczony do dziś), myślę, że to wciąż całkiem dobre dla czytelności: jeśli opiekun zdecyduje się go zbadać, będzie musiał po prostu usunąć go do zobacz błąd, a on (prawdopodobnie) łatwo zrozumie cel tego "czaru". –

+1

@golergka Teraz, wraz z faktycznymi implementacjami, które pokazałeś w pytaniu, twoje przeciążenia '==' mogą eksplodować z 'NullReferenceException'. Może chciałeś wbudowanego przeciążenia 'operator == (obiekt, obiekt)' (nie jest to właściwie metoda w IL)? Myślę, że zrobiłeś. Tak więc jest to 'someObject == (object) null' lub' (object) someObject == null' lub '(object) someObject == (object) null' (wszystkie trzy przechodzą do wbudowanego' == ', nie dowolny z twoich operatorów). –

4

Zamiast definiowania dwóch operatorów równości, można utworzyć niejawna konwersja między string i SomeClass:

class SomeClass 
{ 
    string value; 
    public SomeClass(string _Value) 
    { 
     value = _Value; 
    } 
    static public bool operator ==(SomeClass C1, SomeClass C2) 
    { 
     return C1.value == C2.value; 
    } 
    static public bool operator !=(SomeClass C1, SomeClass C2) 
    { 
     return C1.value != C2.value; 
    } 

    public static implicit operator string(SomeClass instance) 
    { 
     return instance.value; 
    } 

    public static implicit operator SomeClass(string str) 
    { 
     return new SomeClass(str); 
    } 
    //TODO override Equals and GetHashCode to use `value` 
} 

Teraz, jeśli porównać wartość NULL nie ma problemu niejednoznaczności.

Ma to również efekt uboczny tworzenia klas niejawnie zamiennych ze sobą wszędzie, ale opartych na kontekście, że nie wydaje się być złe.

+1

To dobry pomysł. Jednak w tym konkretnym przypadku celem klasy (oczywiście nie tej przedstawionej w pytaniu) było zastąpienie klasy string, aby zapewnić podobną funkcjonalność z dodatkową umową i upewnić się, że łańcuchy nie są używane w pewnym kontekście - tak niejawne konwersje mogą zniszczyć jego cel. Jednak ogólnie rzecz biorąc, to świetne rozwiązanie, dzięki. –

+0

Odmawianie 'someObject == null' nie jest już niejednoznaczne (i nie przechodzi przez' niejawny operator'), ale oczywiście implementacja 'operatora ==' prowadzi do pewnego 'NullReferenceException', ponieważ' C2' będzie 'null'. –

-1

Możesz przekazać drugi parametr jako "obiekt" i sprawdzić jego typ, zanim zdecydujesz, którą równość wykonać.

static public bool operator == (SomeClass C1, object C2) 
{ 
    if(C2 is SomeClass) 
    return C1.value == ((SomeClass)C2).value; 
    else if (C2 is string) 
    return C1.value == (string) C2; 
} 
+0

Ale wtedy możesz również przekazać 'bool', który nie powinien nawet kompilować. Kod – Servy

+0

jest oczywiście częściowy, ponieważ wymaga innego sprawdzenia, aby obsłużyć nieobsługiwane typy, które nie wiem jak OP chce obsłużyć – Konstantin

+0

Reglardless, to byłby tylko sprawdzanie w czasie wykonywania, a nie kontrola czasu kompilacji. Powinna to być kontrola czasu kompilacji. – Servy

1

Dla tych pochodzących późno na to patrz poniżej dla bardziej akceptowalnym odpowiedź, która jest ukrywanie w komentarzach z @Jeppe Stig Nielsen.

PO poprosił konkretnie o nadrzędnego operatora ==, jednak uważam, że jest to ważna informacja, gdy nadrzędne operatora == i uważają, że prawidłowa odpowiedź na przyszłość powinno być: -

Console.WriteLine((object)someObject == null); 

Używając zaakceptowanej odpowiedzi i implementując zarówno == jak i Równe w twoim obiekcie, będziesz nadal otrzymywać ten sam błąd. Najlepiej porównać do wartości null na obiekcie najniższego poziomu, w ten sposób porównujesz "obiekt" do wartości zerowej i wszystkie niejednoznaczności są usuwane z porównania.

Oto powód i rozdzielczość jak na realizowane w MSDN: Guidelines for Overriding Equals() and Operator ==

Rozważmy następujący patrz komentarze w realizacji wynosi: -

class SomeClass 
{ 
    string value; 
    public SomeClass(string _Value) 
    { 
     value = _Value; 
    } 

    static public bool operator ==(SomeClass C1, SomeClass C2) 
    { 
     return C1.value == C2.value; 
    } 

    public override bool Equals(SomeClass C1) 
    { 
     // causes error due to unsure which operator == to use the SomeClass == or the object == 
     // Actual error: Operator '==' is ambiguous on operands of type 'SomeClass' and '<null>' 
     if (C1 == null) 
      return false; 

     // Give same error as above 
     if (C1 == default(SomeClass)) 
      return false; 

     // Removes ambiguity and compares using base objects == to null 
     if ((object)C1 == null) 
      return false; 

     return value == C1.value; 
    } 
} 
Powiązane problemy