La Surcharge d'opérateur avec Interface de Base de la Programmation en C#
Fond
Je suis en utilisant l'interface de programmation basé sur un projet en cours et ont rencontré un problème lors de la surcharge des opérateurs (plus précisément de l'Égalité et de l'Inégalité des opérateurs).
Hypothèses
- Je suis à l'aide de C# 3.0, .NET 3.5 et Visual Studio 2008
Mise à JOUR - L'Hypothèse Suivante a été Faux!
- Exigeant que toutes les comparaisons à utilisation Égale, plutôt que de l'opérateur== est pas une solution viable, en particulier lors du passage de vos types de bibliothèques (comme les Collections).
La raison pour laquelle j'étais inquiète au sujet de l'obligation est Égale à utiliser plutôt que l'opérateur== est que je ne pouvais pas trouver n'importe où dans le .NET des lignes directrices qu'il a déclaré qu'il utiliserait est Égal plutôt que l'opérateur==, ou même suggérer. Cependant, après re-lecture Lignes directrices pour l'Impérieuse d'égal à Égal et de l'Opérateur== j'ai trouvé ceci:
Par défaut, l'opérateur == tests de référence de l'égalité de déterminer si deux références indiquent le même objet. Par conséquent, les types de référence n'ont pas à mettre en œuvre l'opérateur == pour obtenir cette fonctionnalité. Quand un type est immuable, qui est, les données contenues dans l'instance ne peut pas être changé, la surcharge de l'opérateur == pour comparer la valeur de l'égalité au lieu de référence de l'égalité peut être utile parce que, comme des objets immuables, ils peuvent être considérés de la même aussi longtemps qu'ils ont la même valeur. Ce n'est pas une bonne idée de remplacer l'opérateur == non immuable types.
et ce Equatable Interface
La IEquatable interface est utilisée par générique des objets de collection tels que le Dictionnaire, la Liste et les LinkedList lors de l'essai pour l'égalité dans les méthodes telles que Contient, IndexOf, LastIndexOf, et Retirez-la. Il devrait être mis en œuvre pour n'importe quel objet qui peut être stocké dans une collection générique.
Contraintes
- Aucune solution ne doit pas nécessiter de casting les objets de leurs interfaces à leurs types de béton.
Problème
- Quand jamais les deux côtés de l'opérateur== sont une interface, aucun opérateur== surcharge de la signature de la méthode de la sous-jacentes types de béton match et donc la valeur par défaut de l'Objet de l'opérateur== méthode sera appelée.
- En cas de surcharge d'un opérateur sur une classe, au moins l'un des paramètres de l'opérateur binaire doit être du type contenant, sinon une erreur de compilateur est produite (Erreur BC33021 http://msdn.microsoft.com/en-us/library/watt39ff.aspx)
- Il n'est pas possible de spécifier la mise en œuvre sur une interface
Voir le Code et de Sortie ci-dessous montrant le problème.
Question
Comment voulez-vous assurer la surcharge de l'opérateur pour vos classes lors de l'utilisation de l'interface de base de la programmation?
Références
Prédéfinis pour différents types de valeurs, l'opérateur d'égalité (==) retourne true si les valeurs de ses opérandes sont égaux, sinon false. Pour les types de référence autres que string, == retourne true si les deux opérandes font référence au même objet. Pour le type de chaîne, == compare les valeurs des chaînes de caractères.
Voir Aussi
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.");
}
}
}
}
Sortie
Address operator== overload called
Equal with both sides cast.
- Pouvez-vous revenir sur votre deuxième hypothèse? Collection les classes à utiliser .Méthode Equals ().
- +1 pour la clarté et les détails en question.
- kvb - j'ai mis à jour ma deuxième hypothèse, et après la lecture de Jean de la réponse et un peu plus de MSDN docs, l'hypothèse est fausse. J'ai noté ci-dessus. Merci! Cyril, merci de vous!
Vous devez vous connecter pour publier un commentaire.
Réponse courte: je pense que votre deuxième hypothèse peut être imparfaite.
Equals()
est la bonne façon de vérifier sémantique de l'égalité de deux objets, pasoperator ==
.Réponse longue: résolution de Surcharge pour les opérateurs est effectué au moment de la compilation, pas de temps d'exécution.
À moins que le compilateur peut définitivement connaître les types des objets, c'est l'application d'un opérateur, il ne compile pas. Car le compilateur ne peut pas être sûr qu'un
IAddress
va être quelque chose qui a un remplacement pour==
défini, il revient à la valeur par défautoperator ==
mise en œuvre deSystem.Object
.Pour le voir plus clairement, essayez de définir une
operator +
pourAddress
et l'ajout de deuxIAddress
instances. Sauf si vous avez explicitement exprimées àAddress
, il ne pourra pas compiler. Pourquoi? Parce que le compilateur ne peut pas dire qu'un particulierIAddress
est unAddress
, et il n'y a pas de valeur par défautoperator +
de mise en œuvre de retomber dansSystem.Object
.Partie de votre frustration provient probablement du fait que
Object
met en œuvre unoperator ==
, et tout est unObject
, de sorte que le compilateur peut résoudre avec succès les opérations commea == b
pour tous les types. Lorsque vous l'emportaient sur les==
, vous vous attendiez à voir le même comportement, mais n'a pas, et parce que c'est le meilleur match que le compilateur peut trouver l'originalObject
mise en œuvre.À mon avis, c'est précisément ce que vous devriez faire.
Equals()
est la bonne façon de vérifier sémantique de l'égalité de deux objets. Parfois sémantique de l'égalité est tout simplement référence à l'égalité, dans ce cas, vous n'aurez pas besoin de changer quoi que ce soit. Dans d'autres cas, comme dans votre exemple, vous allez remplacerEquals
lorsque vous avez besoin d'une plus forte égalité contrat de référence de l'égalité. Par exemple, vous pourriez envisager de deuxPersons
égales si elles ont le même numéro de Sécurité Sociale, ou deuxVehicles
égales si elles ont le même VIN.Mais
Equals()
etoperator ==
ne sont pas la même chose. Chaque fois que vous en avez besoin pour remplaceroperator ==
, vous devez remplacerEquals()
, mais presque jamais l'inverse.operator ==
est plus d'une syntaxe de commodité. Certains CLR langues (par exemple, Visual Basic.NET) ne pas même vous permettre de remplacer l'opérateur d'égalité.Is
pour la référence à l'égalité, au lieu de partager=
comme C#.Object
. Au lieu de cela, il existe deux opérateurs de==
peut représenter--soit un overloadable opérateur d'égalité ou de non-overloadable référence de comparaison--et C# va essayer d'utiliser le deuxième lors de la première tentative échoue. Pas un design, je l'aime, depuisbool CheckEquals<T>(T p1, T p2) where T:class {return p1 == p2;}
, appelantCheckEquals("123", 123.ToString());
serait de vérifier l'identité des références plutôt que des valeurs correspondantes, même si==
avecString
normalement compare des valeurs. Dans quelque chose comme VB.NET, ...Is
, mais dans ce cas, il serait évident qu'il teste pour référence d'identité.Nous avons rencontré le même problème et trouvé une excellente solution: Resharper modèles personnalisés.
Nous avons configuré l'ENSEMBLE de nos utilisateurs à utiliser un commun mondial, catalogue de motif, en plus de leur propre, et l'a placé dans le SVN de sorte qu'il peut être versionnés et mis à jour pour tout le monde.
Le catalogue inclus tous les modèles connus pour être mal dans notre système:
$i1$ == $i2$
(où i1 et i2 sont expressions de notre type d'interface, ou dérivé.le remplacer motif est
$i1$.Equals($i2$)
et la gravité est de "Montrer que l'erreur".
De même, nous avons
$i1$ != $i2$
Espère que cette aide.
P. S. catalogues Globaux, est la caractéristique dans Resharper 6.1 (PAE), sera marqué comme final très bientôt.
Mise à jour: j'ai déposé une Resharper Problème de marquer l'ensemble de l'interface '==' un avertissement, sauf si c'est la comparaison à la valeur null. Merci de voter si vous pensez qu'il est digne de la fonction.
Update2: Resharper a aussi [CannotApplyEqualityOperator] attribut qui peut vous aider.