2013-03-07 10 views
9

ho trovato una domanda similecorrettamente implementare il confronto di due oggetti di tipo diverso ma semanticamente equivalenti

How to compare two distinctly different objects with similar properties

che possono implicitamente e/o in parte, una risposta alla mia domanda.

Supponiamo che io voglio confrontare (senza un sacco di condizioni nidificate) questo oggetto:

class ObjectA { 
    public string PropertyX { get; set; } 
    public char PropertyY { get; set; } 
    public long PropertyZ { get; set; } 
} 

ad un System.String. Sono interessato solo a uguaglianza o disuguaglianza (non un intervallo di valori sull'identità).

L'implementazione di IEquatable<string> in ObjectA è una scelta appropriata? Non mi interessa cosa semplicemente funziona, voglio identificare il modello corretto per tale caso.

Come altre informazioni, si prega di considerare che ObjectA sarà spesso fornito come sequenza di IEnumerable<ObjectA>.

Non ho bisogno di sapere se "string"== o !=objectA istanza; l'ordinamento non è coinvolto.

Modifica per chiarire (e aiuto)

Ci dispiace, ma scrivere una buona domanda è a volte difficile ...

Supponiamo che non posso rappresentare ObjectA come stringa a scopo di confronto (violare l'incapsulamento non è un'opzione).

  • In context-1 Devo corrispondere a PropertyY.

  • In context-2 devo confrontarlo con un algoritmo applicato a PropertyY/PropertyZ.

@Oliver soluzione alla fine della domanda mi aiuta ancora (e di nuovo 1). posso semplicemente definire un'interfaccia personalizzata:

interface IContextConverter { 
    string ToEquatableStringForContext1(); 
    string ToEquatableStringForContext2(); 
} 

dal momento che ho anche un ObjectB con stessa logica, ma diverse proprietà, entrambi saranno implementare IContextConverter (o forse trovare un nome migliore) evitando di violare RAP.

+0

Penso che sia necessario sovrascrivere il metodo Equals, come si vede in http://stackoverflow.com/questions/4219261/overriding-operator-how-to-compare-to-null. – user1929959

+0

Cosa intendi? Se 'System.String' è' "Hello mum!" ', E' ObjectA' ha x, y, z uguale a '" Cool "', ''Z'' e' 42L', come faccio a capire fuori se sono uguali o no? –

+1

Non ne sono sicuro. Penso che verrà usato impropriamente poiché è progettato per confrontare oggetti dello stesso tipo ... Guarda il confronto dei tipi con l'operatore (== per esempio). L'oggetto nel LHS e l'altro nel RHS sono dello stesso tipo. – jay

risposta

5

consiglio vivamente a non implementare IEquatable<string>, causare soprattutto quando si lavora con le collezioni , dizionari, LINQ, ecc. non sai davvero quando uno di questi metodi sarà chiamato da qualche parte nel profondo che porta forse a bug sottili.

A causa del fatto che ti piace confrontare due oggetti di diverso tipo, un semplice Comparer<T> non funziona anche.

Quindi o scrivere TypeConverter che converte l'oggetto nel tipo desiderato (nel caso di un string) o aggiungere un metodo all'oggetto come .ToEquatableString() e utilizzare la loro produzione di confrontare l'oggetto con l'altra stringa.

Ecco un esempio su di voi potrebbe ottenere tutti gli elementi, che corrispondono a uno di una stringa in un'altra raccolta:

IEnumerable<String> otherElements = new[] {"abc", "def", "ghi" }; 
IEnumerable<ObjectA> myObjects = GetObjects(); 

var matchesFound = otherElements.Join(// Take the first collection. 
       myObjects, // Take the second collection. 
       s => s, // Use the elements in the first collection as key (the string). 
       obj => obj.ToEquatableString(), // Create a string from each object for comparison. 
       (s, obj) => obj, // From the matching pairs take simply the objects found. 
       StringComparer.OrdinalIgnoreCase); // Use a special string comparer if desired. 
+0

+1, @Oliver, ToEquatableString() restituisce un wrapper di ObjectA che implementa IEquatable ? – jay

+2

@jay: No. Restituisce una stringa che rappresenta 'oggettoA'. Quindi essenzialmente lo stesso come '.ToString()' fa. Questa stringa può essere utilizzata come qualsiasi altra stringa per il confronto. – Oliver

+0

+1 per la soluzione [KISS] (http://en.wikipedia.org/wiki/KISS_principle). – publicgk

1

NOTA: Non raccomando particolarmente questa soluzione per questo caso particolare, ma ho spesso utilizzato questo framework per implementare Value Equality per le strutture. I trade-off difficili sono comuni nel nostro campo e le risposte che si rivolgono a quelle, accompagnate da opportuni avvertimenti, sembrano in ordine.

Suppongo che desideri progettare la semantica di Value Equality sulla tua classe nello stesso modo in cui .NET framework fa ciò per la classe string.Come minimo, quanto segue è necessario:

public override bool Equals(object obj) { 
    return (obj is ObjectA) && this == (ObjectA)obj; 
} 
bool IEquatable<ObjectA>.Equals(ObjectA rhs) { 
    return this == rhs; 
} 
public static bool operator != (ObjectA lhs, ObjectA rhs) { 
    return ! (lhs == rhs); 
} 
public static bool operator == (ObjectA lhs, ObjectA rhs) { 
    return (lhs.PropertyX == rhs.PropertyX); 
} 
public override int GetHashCode() { 
    return PropertyX.GetHashCode() 
} 

espansione per consentire il confronto di valore beween Objecta e stringa:

bool IEquatable<ObjectA>.Equals(string rhs) { 
    return this == rhs; 
} 
public static bool operator != (ObjectA lhs, string rhs) { 
    return ! (lhs == rhs); 
} 
public static bool operator != (string lhs, ObjectA rhs) { 
    return ! (lhs == rhs); 
} 
public static bool operator == (ObjectA lhs, string rhs) { 
    return (lhs.PropertyX == rhs); 
} 
public static bool operator == (string lhs, ObjectA rhs) { 
    return (lhs == rhs.PropertyX); 
} 
+0

+1 per il codice di esempio. Ma ancora nel dubbio che questa è la soluzione. La mia necessità principale è di cercare un oggetto IEnumerable utilizzando System.String senza spostare la logica di confronto nella query di Linq. – jay

+1

@jay: vedi aggiornamento. Potresti incontrare difficoltà se il framework tenta di eseguire string.Equals (ObjectA) anziché ObjectA.Equals (stringa).Non lo consiglio, poiché è probabile che dipenda dall'implementazione specifica della versione del framework. –

+0

questo spiega meglio la soluzione. Apprezzato. – jay

1

ci sono molte possibilità.

Se ti senti un ObjectA è una sorta di System.String, si potrebbe scrivere una conversione definita dall'utente (implicita o esplicita) ObjectA-System.String, o System.String-ObjectA, o entrambe le direzioni.

È inoltre possibile sovraccaricare gli operatori == e != con una firma come operator ==(ObjectA oa, string s). Fai attenzione che c'è differenza tra oa == s e s == oa.

Una di queste due possibilità può causare confusione. Sarebbe anche confuso sovrascrivere il virtuale Equals(object) o introdurre un sovraccarico Equals(string). Pertanto non consiglio l'implementazione di IEquatable<string>.

Perché non scrivere semplicemente un metodo con un nome non utilizzato, ad esempio public bool EqualsString(string s)? Quindi dovrai chiamare questo metodo in modo esplicito, ovviamente, ma ciò porterà a una minore confusione. Un'altra idea sarebbe quella di utilizzare un costruttore della firma public ObjectA(string s) e quindi implementare l'uguaglianza "omogenea" di ObjectA e ObjectA.

+0

+1 @Jeppe Stig Neilsen, è piaciuta la soluzione 'ObjectA :: EqualsString (string)'. Porta al codice che è semplice e chiaro da leggere (e come più imparo, questa è la mia priorità). – jay