Hintergrund
Ich verwende die schnittstellenbasierte Programmierung für ein aktuelles Projekt und bin beim Überladen von Operatoren (insbesondere der Operatoren für Gleichheit und Ungleichheit) auf ein Problem gestoßen.
Annahmen
- Ich verwende C # 3.0, .NET 3.5 und Visual Studio 2008
UPDATE - Die folgende Annahme war falsch!
- Das Erfordernis aller Vergleiche zur Verwendung von Equals anstelle von operator == ist keine praktikable Lösung, insbesondere wenn Sie Ihre Typen an Bibliotheken (z. B. Sammlungen) übergeben.
Der Grund, warum ich besorgt war, dass Equals anstelle von operator == verwendet werden muss, ist, dass ich in den .NET-Richtlinien nirgendwo finden konnte, dass Equals anstelle von operator == verwendet oder sogar vorgeschlagen wird. Nach dem erneuten Lesen der Richtlinien zum Überschreiben von Gleichheit und Operator == habe ich Folgendes gefunden:
Standardmäßig prüft der Operator == die Referenzgleichheit, indem er ermittelt, ob zwei Referenzen dasselbe Objekt angeben. Daher müssen Referenztypen den Operator == nicht implementieren, um diese Funktionalität zu erhalten. Wenn ein Typ unveränderlich ist, dh die in der Instanz enthaltenen Daten nicht geändert werden können, kann das Überladen des Operators == zum Vergleichen der Wertgleichheit anstelle der Referenzgleichheit nützlich sein, da sie als unveränderliche Objekte als gleich lange betrachtet werden können da sie den gleichen Wert haben. Es ist keine gute Idee, operator == in nicht unveränderlichen Typen zu überschreiben.
und diese gleichwertige Schnittstelle
Die IEquatable-Schnittstelle wird von generischen Auflistungsobjekten wie Dictionary, List und LinkedList verwendet, wenn die Gleichheit in Methoden wie Contains, IndexOf, LastIndexOf und Remove getestet wird. Es sollte für jedes Objekt implementiert werden, das möglicherweise in einer generischen Sammlung gespeichert ist.
Einschränkungen
- Für jede Lösung muss es nicht erforderlich sein, die Objekte von ihren Schnittstellen auf ihre konkreten Typen zu werfen.
Problem
- Immer wenn beide Seiten des Operators == eine Schnittstelle sind, stimmt keine Signatur der Operator == -Überladungsmethode der zugrunde liegenden konkreten Typen überein, und daher wird die Standardmethode Object operator == aufgerufen.
- Beim Überladen eines Operators für eine Klasse muss mindestens einer der Parameter des binären Operators der enthaltende Typ sein, andernfalls wird ein Compilerfehler generiert (Fehler BC33021 http://msdn.microsoft.com/en-us/library/watt39ff .aspx )
- Es ist nicht möglich, die Implementierung auf einer Schnittstelle anzugeben
Siehe Code und Ausgabe unten, um das Problem zu demonstrieren.
Frage
Wie stellen Sie bei Verwendung der Schnittstellenbasisprogrammierung die richtigen Operatorüberladungen für Ihre Klassen bereit?
Verweise
Für vordefinierte Werttypen gibt der Gleichheitsoperator (==) true zurück, wenn die Werte seiner Operanden gleich sind, andernfalls false. Für andere Referenztypen als Zeichenfolge gibt == true zurück, wenn sich die beiden Operanden auf dasselbe Objekt beziehen. Für den Zeichenfolgentyp vergleicht == die Werte der Zeichenfolgen.
Siehe auch
Code
using System;
namespace OperatorOverloadsWithInterfaces
{
public interface IAddress : IEquatable<IAddress>
{
string StreetName { get; set; }
string City { get; set; }
string State { get; set; }
}
public class Address : IAddress
{
private string _streetName;
private string _city;
private string _state;
public Address(string city, string state, string streetName)
{
City = city;
State = state;
StreetName = streetName;
}
#region IAddress Members
public virtual string StreetName
{
get { return _streetName; }
set { _streetName = value; }
}
public virtual string City
{
get { return _city; }
set { _city = value; }
}
public virtual string State
{
get { return _state; }
set { _state = value; }
}
public static bool operator ==(Address lhs, Address rhs)
{
Console.WriteLine("Address operator== overload called.");
// If both sides of the argument are the same instance or null, they are equal
if (Object.ReferenceEquals(lhs, rhs))
{
return true;
}
return lhs.Equals(rhs);
}
public static bool operator !=(Address lhs, Address rhs)
{
return !(lhs == rhs);
}
public override bool Equals(object obj)
{
// Use 'as' rather than a cast to get a null rather an exception
// if the object isn't convertible
Address address = obj as Address;
return this.Equals(address);
}
public override int GetHashCode()
{
string composite = StreetName + City + State;
return composite.GetHashCode();
}
#endregion
#region IEquatable<IAddress> Members
public virtual bool Equals(IAddress other)
{
// Per MSDN documentation, x.Equals(null) should return false
if ((object)other == null)
{
return false;
}
return ((this.City == other.City)
&& (this.State == other.State)
&& (this.StreetName == other.StreetName));
}
#endregion
}
public class Program
{
static void Main(string[] args)
{
IAddress address1 = new Address("seattle", "washington", "Awesome St");
IAddress address2 = new Address("seattle", "washington", "Awesome St");
functionThatComparesAddresses(address1, address2);
Console.Read();
}
public static void functionThatComparesAddresses(IAddress address1, IAddress address2)
{
if (address1 == address2)
{
Console.WriteLine("Equal with the interfaces.");
}
if ((Address)address1 == address2)
{
Console.WriteLine("Equal with Left-hand side cast.");
}
if (address1 == (Address)address2)
{
Console.WriteLine("Equal with Right-hand side cast.");
}
if ((Address)address1 == (Address)address2)
{
Console.WriteLine("Equal with both sides cast.");
}
}
}
}
Ausgabe
Address operator== overload called
Equal with both sides cast.