Frage

Hintergrund

Ich bin mit Interface-basierten Programmierung an einem aktuellen Projekt und habe sie zu einem Problem führen, wenn Betreiber (insbesondere die Gleichheit und Ungleichheit Operator) zu überlasten.


Annahmen

  • Ich bin mit C # 3.0, .NET 3.5 und Visual Studio 2008

UPDATE - Die folgende Annahme war falsch

  • , wonach alle Vergleiche Verwendung Equals anstatt Operator == ist keine gangbare Lösung, vor allem, wenn Ihre Typen Bibliotheken (wie zB Sammlungen) vorbei.

Der Grund, warum ich war besorgt über Equals erfordern eher als Operator verwendet wird == ist, dass ich nicht überall in den .NET-Richtlinien finden konnte, die es festgestellt, es Equals eher als Operator verwenden würde == oder sogar vorschlagen. Doch nach dem erneuten Lesen Richtlinien für Übergeordnete Equals und Operator == I diese haben gefunden:

  

Standardmäßig ist der Operator == Tests als Referenz Gleichheit durch die Bestimmung, ob zwei Referenzen auf das gleiche Objekt zeigen. Daher Referenztypen müssen nicht Operator == implementieren, um diese Funktionalität zu erhalten. Wenn ein Typ unveränderlich ist, das heißt, die Daten, die in der Instanz enthalten ist, können nicht geändert werden, Überladen von Operatoren == auf Wertgleichheit statt Bezug Gleichheit vergleichen kann nützlich sein, weil, als unveränderliche Objekte, können sie die gleichen wie lange betrachtet werden da sie den gleichen Wert haben. Es ist keine gute Idee, Operator == in nicht-unveränderlichen Typen außer Kraft zu setzen.

und diese gleichzusetzen Schnittstelle

  

Die IEquatable Schnittstelle, die durch generische Auflistung verwendet wird, Objekte wie Wörterbuch, List und LinkedList, wenn für die Gleichstellung in solchen Verfahren zu testen, wie Enthält, IndexOf, LastIndexOf und entfernen. Es sollte für jedes Objekt implementiert werden, die in einer generischen Sammlung gespeichert werden könnten.


Contraints

  • Jede Lösung muss nicht benötigen, um die Objekte von ihren Schnittstellen zu ihren konkreten Typen Gießen.

Problem

  • Wann immer beide Seiten der Operator == eine Schnittstelle sind, wird kein Operator == Überlast-Methode Signatur aus den zugrundeliegenden Betontypen entsprechen und somit wird die Standard-Objekt Operator == Methode aufgerufen werden.
  • Wenn auf einer Klasse eine Bedienungsperson zu überlasten, wobei mindestens einer der Parameter des binären Operators muss der Typ enthalten sein, da sonst ein Compiler-Fehler generiert wird (Error BC33021 http://msdn.microsoft.com/en-us/library/watt39ff.aspx )
  • Es ist nicht möglich, die Implementierung auf einer Schnittstelle zu spezifizieren

Siehe-Code und Output unter dem Thema zu demonstrieren.


Frage

Wie Sie die richtigen Betreiber Überlastungen für Ihre Klassen bieten, wenn Interface-Basisprogrammierung mit?


Referenzen

== Operator (C # -Referenz)

Bei vordefinierten Wertetypen, der Gleichheitsoperator (==) gibt true zurück, wenn die Werte der Operanden gleich sind, andernfalls false. Als Referenz andere Typen als String, gibt == true, wenn seine zwei Operanden auf das gleiche Objekt beziehen. Für den String-Typ vergleicht == die Werte der Strings.


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.
War es hilfreich?

Lösung

Kurze Antwort: Ich denke, Ihre zweite Annahme fehlerhaft sein kann. Equals() ist der richtige Weg für semantische Gleichheit von zwei Objekten zu überprüfen, nicht operator ==.


Lange Antwort:. Die Überladungsauflösung für die Betreiber ist zum Zeitpunkt der Kompilierung durchgeführt wird, keine Zeit laufen

Wenn der Compiler endgültig die Typen der Objekte kennt, kann es einem Bediener, ist die Anwendung, wird es nicht kompilieren. Da der Compiler nicht sicher sein kann, dass ein IAddress etwas sein wird, die eine Überschreibung für == hat definiert, fällt es zurück auf den Standard operator == Implementierung von System.Object.

Um dies zu sehen deutlicher, versuchen, eine operator + für Address definieren, und das Hinzufügen von zwei IAddress Instanzen. Wenn Sie nicht explizit auf Address werfen, wird es nicht kompilieren. Warum? Da der Compiler kann nicht sagen, dass eine bestimmte IAddress ein Address ist, und es gibt keine Standard operator + Implementierung zurück in System.Object zu fallen.


Ein Teil Ihrer Frustration stammt vermutlich aus der Tatsache, dass Object eine operator == implementiert, und alles ist ein Object, so dass der Compiler erfolgreich Operationen wie a == b für alle Arten auflösen kann. Wenn Sie == overrode, erwartet Sie das gleiche Verhalten zu sehen, aber nicht, und das ist, weil die beste Übereinstimmung der Compiler die ursprüngliche Object Implementierung gibt.

  

, wonach alle Vergleiche Verwendung Equals anstatt Operator == ist keine gangbare Lösung, vor allem, wenn Ihre Typen Bibliotheken (wie zB Sammlungen) vorbei.

Aus meiner Sicht ist dies genau das, was Sie tun sollen. Equals() ist der richtige Weg für semantische Gleichheit von zwei Objekten zu überprüfen. Manchmal semantische Gleichheit ist nur Gleichheit verweisen, in dem Fall, dass Sie nichts ändern müssen. In anderen Fällen, wie in Ihrem Beispiel werden Sie Equals außer Kraft setzen, wenn Sie eine stärkere Gleichstellung Vertrag als Referenz Gleichheit müssen. Zum Beispiel können Sie zwei Persons gleich prüfen, ob sie die gleiche Sozialversicherungsnummer haben, oder zwei Vehicles gleich, wenn sie die gleiche VIN haben.

Aber Equals() und operator == ist nicht dasselbe. Wann immer Sie brauchen operator == außer Kraft zu setzen, sollten Sie außer Kraft setzen Equals(), aber so gut wie nie umgekehrt. operator == ist eher eine syntaktische Bequemlichkeit. Einige CLR Sprachen (zum Beispiel Visual Basic.NET) nicht einmal erlauben Sie den Gleichheitsoperator außer Kraft zu setzen.

Andere Tipps

Wir liefen in das gleiche Problem und fand eine hervorragende Lösung. ReSharper individuelle Muster

Ich konfigurierte alle unsere Nutzer einen gemeinsamen globales Muster Katalog zusätzlich zu verwenden, um ihre eigenen, und legte sie in SVN, so dass es versioniert werden kann und für alle aktualisiert.

Der Katalog enthielt alle Muster in unserem System falsch ist bekannt:

$i1$ == $i2$ (wobei i1 und i2 sind Ausdrücke unserer Interface-Typ, oder abgeleitet.

das ersetzen Muster

$i1$.Equals($i2$)

und die Schwere ist "Show als Fehler".

Ebenso haben wir $i1$ != $i2$

Hoffe, das hilft. P. S. Globale Kataloge ist die Funktion in ReSharper 6.1 (EAP), wird sehr bald als endgültig markiert werden.

Aktualisieren : Ich reichte eine ReSharper Ausgabe markieren alle Schnittstelle ‚==‘ eine Warnung, wenn es auf null vergleicht. Bitte wählen, wenn Sie denken, es ist eine würdige Funktion.

Update2 . ReSharper hat auch [CannotApplyEqualityOperator] Attribut, das helfen kann,

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top