2013-08-05 11 views
8

Ho una classe con due sostituzioni per operatore ==, per confrontarla con altre istanze di questa classe e per confrontarla con l'istanza della stringa.Il controllo Null è ambiguo per una classe con più sostituzioni per operatore ==

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; 
    } 
} 

Tuttavia, quando provo a confrontare questa classe per nulla:

 Console.WriteLine(someObject == null); 

ottengo il seguente errore:

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

Come devo definire il mio == sovrascrive in modo che posso controlla ancora le istanze di questa classe?

+1

Forse c'è qualcosa di intelligente che si può fare con un [ null coalesce] (http://msdn.microsoft.com/en-us/library/ms173224.aspx) – tnw

risposta

6

Poiché si sta utilizzando un valore letterale null, il compilatore non sa quale metodo chiamare poiché sia ​​string sia SomeClass possono essere nulli.

Una tecnica per forzare il compilatore a scegliere uno dei metodi è digitare il valore null.

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

O meglio ancora, invece di utilizzare null esplicitamente, utilizzare la parola chiave default per ottenere il valore nullo (perché default(T) è nullo quando T è un tipo di riferimento).

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

Questo si è dimostrato il modo più elegante, grazie. Anche se (SomeClass) null può sembrare strano nel codice (ne sarei certamente sorpreso fino ad oggi), penso che sia ancora abbastanza buono per la leggibilità: se il maintainer decide di esplorarlo, dovrà semplicemente rimuoverlo vedi l'errore, e lui (presumibilmente) capirà facilmente lo scopo di questo "incantesimo". –

+1

@golergka Ora, con le effettive implementazioni mostrate nella domanda, i sovraccarichi di '==' potrebbero esplodere entrambi con una 'NullReferenceException'. Forse volevi che il gestore di overload incorporato C# == (oggetto, oggetto) '(in realtà non è un metodo nell'IL)? Penso che tu l'abbia fatto Quindi 'someObject == (oggetto) null' o' (oggetto) someObject == null' o '(oggetto) someObject == (oggetto) null' (tutti e tre vanno al built-in' == ', non uno dei tuoi operatori). –

4

Piuttosto che definire due operatori di uguaglianza, è possibile creare una conversione implicita tra string e 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` 
} 

Ora, quando si confronta il valore con null non v'è alcun problema di ambiguità.

Questo ha anche l'effetto collaterale di rendere le classi implicitamente convertibili l'una con l'altra ovunque, ma basate sul contesto che non sembra essere una cosa negativa.

+1

Questa è una buona idea. Tuttavia, in questo caso particolare l'obiettivo della classe (ovviamente, non quello presentato nella domanda) era di sostituire la classe stringa in modo da fornire funzionalità simili con un contratto aggiuntivo e assicurarsi che le stringhe non vengano utilizzate in un determinato contesto - conversioni così implicite avrebbero vanificato il suo scopo. Tuttavia, in generale, questa è un'ottima soluzione, grazie. –

+0

Dire 'someObject == null' non è più ambiguo (e non passa attraverso' operatore implicito'), ma ovviamente l'implementazione di 'operator ==' porta ad un sicuro 'NullReferenceException' perché' C2' sarà 'null'. –

-1

È possibile passare il 2 ° parametro come "oggetto" e controllarne il tipo prima di decidere quale uguaglianza fare.

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

Ma potreste anche passare in un 'bool', e questo non dovrebbe nemmeno essere compilato. Il codice – Servy

+0

è ovviamente parziale in quanto ha bisogno di un altro controllo per gestire i tipi non supportati, che non so come OP vuole gestire – Konstantin

+0

Reglardless, sarebbe solo un controllo di runtime, non un controllo del tempo di compilazione. Dovrebbe essere un controllo del tempo di compilazione. – Servy

1

Per chi arriva in ritardo per questo si rimanda sotto per la risposta più accettabile che è Hidding in commenti da @Jeppe Stig Nielsen.

Il PO ha chiesto specificamente di operatore primario ==, però, credo che questo è un pezzo importante di informazioni quando sovrascrivendo l'operatore == e credere la risposta corretta per riferimento futuro dovrebbe essere: -

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

Utilizzando la risposta accettata e implementando sia == che Equals nel proprio oggetto, si continuerà a ottenere lo stesso errore. Meglio da confrontare con null all'oggetto di livello più basso, in questo modo si confronta "oggetto" con null e tutte le ambiguità vengono rimosse dal confronto.

Ecco la ragione e la risoluzione come da attuare in MSDN: Guidelines for Overriding Equals() and Operator ==

Si consideri il seguente, consultare i commenti per l'attuazione Equals: -

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; 
    } 
} 
Problemi correlati