342 votes

Pourquoi devons-nous définir à la fois == et! = En C #?

Le compilateur C# nécessite que chaque fois qu'un type personnalisé définit l'opérateur ==, il doit également définir != (voir ici).

Pourquoi?

Je suis curieux de savoir pourquoi les concepteurs ont pensé qu'il était nécessaire et pourquoi pas le compilateur par défaut pour une mise en œuvre raisonnable pour les opérateurs lors que l'autre est présent. Par exemple, Lua permet de définir uniquement l'opérateur d'égalité et vous obtenez le autres gratuitement. C# pourrait faire de même en vous demandant de définir == ou les deux == et != et puis compiler automatiquement le manque != opérateur de !(left == right).

Je comprends qu'il y a de bizarre coin des cas où certaines entités ne peut être égale ou inégale (comme de la norme IEEE-754 NaN), mais celles-ci semblent comme l'exception, pas la règle. Donc, cela n'explique pas pourquoi le compilateur C# concepteurs fait de l'exception la règle.

J'ai vu des cas de mauvaise qualité de l'exécution, où l'opérateur d'égalité est définie, alors l'opérateur d'inégalité est un copier-coller à chaque comparaison inversé et tout && passé à l' || (vous obtenez le point... en fait !(a==b) étendu par le biais De Morgan règles). C'est une mauvaise pratique que le compilateur pourrait éliminer par la conception, comme c'est le cas avec Lua.

Note: La même chose vaut pour les opérateurs < > <= >=. Je ne peux pas imaginer des cas où vous aurez besoin de les définir de façon anormale. Lua permet de définir uniquement les < et <= et définit >= et > naturellement, à travers les faiseurs de " déni. Pourquoi ne pas en C# en faire de même (au moins "par défaut")?

MODIFIER

Apparemment il y a des raisons valables de permettre au programmeur de mettre en œuvre des contrôles de l'égalité et de l'inégalité mais ils aiment. Certaines des réponses point pour les cas où c'est peut-être belle.

Le noyau de ma question, cependant, ce est pourquoi ce est la force nécessaire en C# quand habituellement, il n'est pas logiquement nécessaire?

Il est également en contraste frappant avec les choix de conception .NET les interfaces Object.Equals, IEquatable.Equals IEqualityComparer.Equals où le manque de NotEquals homologue montre que le cadre considère !Equals() des objets que l'inégalité et c'est tout. En outre, des classes comme l' Dictionary et des méthodes comme .Contains() dépendent exclusivement sur lesdites interfaces et de ne pas utiliser les opérateurs directement, même si elles sont définies. En fait, quand ReSharper génère de l'égalité des membres, il définit à la fois == et != en termes de Equals() , et même alors, seulement si l'utilisateur choisit de générer des opérateurs à tous. Les opérateurs d'égalité ne sont pas requises par le cadre pour comprendre l'objet de l'égalité.

Fondamentalement, l' .NET framework ne se soucie pas de ces opérateurs, il se soucie uniquement de quelques Equals méthodes. La décision d'exiger à la fois == et != les opérateurs à être défini en tandem par l'utilisateur est lié uniquement à la conception de langage et non à l'objet de la sémantique dans la mesure où .NET est concerné.

160voto

Je ne peux pas parler la langue des concepteurs, mais de ce que je peux la raison, il me semble que c'était intentionnel, une bonne décision de conception.

En regardant cette base de code F#, vous pouvez le compiler dans un groupe de travail de la bibliothèque. C'est légal, code F#, et seulement les surcharges de l'opérateur d'égalité, de ne pas l'inégalité:

module Module1

type Foo() =
    let mutable myInternalValue = 0
    member this.Prop
        with get () = myInternalValue
        and set (value) = myInternalValue <- value

    static member op_Equality (left : Foo, right : Foo) = left.Prop = right.Prop
    //static member op_Inequality (left : Foo, right : Foo) = left.Prop <> right.Prop

Ce n'exactement à quoi il ressemble. Il crée un comparateur d'égalité sur == seulement, et vérifie si les valeurs internes de la classe sont égaux.

Si vous ne pouvez pas créer une classe comme ça en C#, vous pouvez utiliser celle qui a été compilé .NET. Il est évident qu'il va utiliser notre opérateur surchargé pour == Alors, que fait le moteur d'exécution d'utilisation pour !=?

Le C# EMCA standard a tout un tas de règles (section 14.9) en expliquant comment déterminer quel opérateur à utiliser lors de l'évaluation de l'égalité. Pour le mettre trop simplifiée et donc pas parfaitement exacte, si les types qui sont comparés sont de même type et il y a une surcharge opérateur d'égalité présent, il utilisera que la surcharge et la norme de référence de l'opérateur d'égalité héritées de l'Objet. Il n'est pas surprenant, alors, que si seulement l'un des opérateurs est présent, il va utiliser la valeur par défaut référence d'opérateur d'égalité, que tous les objets ont, il n'y a pas une surcharge.1

Sachant que c'est le cas, la vraie question est: Pourquoi en était-il conçu de cette façon et pourquoi ne pas le compilateur figure sur son propre? Beaucoup de gens disent que ce n'était pas une décision de conception, mais j'aime à penser qu'il a été pensé de cette façon, surtout concernant le fait que tous les objets ont une valeur par défaut opérateur d'égalité.

Alors, pourquoi ne pas le compilateur automatiquement créer l' != opérateur? Je ne peux pas savoir à coup sûr, à moins que quelqu'un de Microsoft le confirme, mais c'est ce que je peux déterminer, à partir d'un raisonnement sur les faits.


Pour éviter un comportement inattendu

Peut-être que je veux faire une comparaison de la valeur sur == pour tester l'égalité. Cependant, quand il est venu à l' != je n'ai pas de soins si les valeurs sont égales, à moins que la référence est égal, parce que pour mon programme à les considérer comme des égaux, je ne soins si les références correspondent. Après tout, ce n'est effectivement décrit comme un comportement par défaut du C# (si les deux opérateurs n'ont pas été surchargé, comme il le serait dans le cas de certains .net des bibliothèques écrites dans une autre langue). Si le compilateur a été l'ajout dans le code automatiquement, je ne pouvais plus compter sur le compilateur de code de sortie qui devrait est conforme. Le compilateur ne doit pas écrire un code caché qui modifie le comportement de la vôtre, en particulier lorsque le code que vous avez écrit est dans les normes du C# et de la CLI.

En termes de forçage vous surcharger, au lieu de revenir au comportement par défaut, je ne peux que dire fermement qu'il est dans la norme (EMCA-334 17.9.2)2. La norme ne précise pas pourquoi. Je crois que cela est dû au fait que le C# emprunte beaucoup le comportement de C++. Voir ci-dessous pour en savoir plus sur cette.


Lorsque vous remplacez != et ==, vous n'avez pas à revenir bool.

C'est une autre raison probable. En C#, cette fonction:

public static int operator ==(MyClass a, MyClass b) { return 0; }

est aussi valable que celui-ci:

public static bool operator ==(MyClass a, MyClass b) { return true; }

Si vous êtes de retour quelque chose d'autre que bool, le compilateur ne peut pas automatiquement déduire un type opposé. En outre, dans le cas où votre opérateur n' retour bool, il n'a tout simplement pas de sens pour les créer générer du code qui n'existe que dans un cas précis, ou, comme je l'ai dit ci-dessus, le code qui se cache le comportement par défaut de la CLR.


C# emprunte beaucoup à partir de C++3

Quand C# a été introduit, il y avait un article dans MSDN magazine qui a écrit, parlant du C#:

De nombreux développeurs souhaite qu'il y ait un langage facile à lire, écrire, et de maintenir comme Visual Basic, mais qui offrait encore la puissance et la flexibilité de C++.

Oui l'objectif de conception pour C# était de donner à peu près la même quantité d'énergie que C++, en sacrifiant un peu seule pour le confort comme de type rigide de la sécurité et de garbage collection. C# a été fortement calquée sur le C++.

Vous ne pouvez pas être surpris d'apprendre qu'en C++, les opérateurs d'égalité n'avez pas à revenir bool, comme le montre cet exemple de programme

Maintenant, C++ ne prend pas directement besoin de vous surcharger le gratuit de l'opérateur. Si votre compilé le code dans le programme d'exemple, vous verrez qu'il s'exécute sans erreur. Toutefois, si vous avez essayé d'ajouter la ligne:

cout << (a != b);

vous obtiendrez

erreur de compilateur C2678 (MSVC) : binary '!=' : l'opérateur n'a pas trouvé ce qui prend de la main gauche opérande de type "Test" (ou il n'est pas acceptable de conversion)`.

Ainsi, tandis que le C++ lui-même n'exige pas de vous surcharger en paires, il ne sera pas vous permettre d'utiliser un opérateur d'égalité que vous n'avez pas surchargé sur une classe personnalisée. C'est valable dans .NET, parce que tous les objets ont une valeur par défaut; C++ ne prend pas.


1. Comme une note côté, le C# standard, vous surcharger la paire d'opérateurs si vous voulez surcharger. C'est une partie de la norme et non simplement par le compilateur. Cependant, les mêmes règles concernant la détermination de l'opérateur qui appeler s'appliquent lorsque vous accédez à un .net-library écrit dans une autre langue que n'ont pas les mêmes exigences.

2. EMCA-334 (pdf) (http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-334.pdf)

3. Et Java, mais ce n'est vraiment pas le point ici

51voto

Yuck Points 23174

Probablement pour si quelqu'un a besoin de mettre en œuvre une logique à trois valeurs (par exemple null ). Dans des cas comme celui-là - SQL standard ANSI, par exemple - les opérateurs ne peuvent pas simplement être annulés en fonction de l'entrée.

Vous pourriez avoir un cas où:

 var a = SomeObject();
 

Et a == true renvoie false et a == false renvoie également false .

25voto

Brian Gordon Points 2551

Autres que que C# reporte à C++ dans de nombreux domaines, la meilleure explication que je peux penser, c'est que, dans certains cas, vous voudrez peut-être prendre une légèrement différente de l'approche à prouver "pas d'égalité" que de prouver l'existence de "l'égalité".

Évidemment, avec de la ficelle de comparaison, par exemple, vous pouvez tester la qualité de l'égalité et de l' return hors de la boucle lorsque vous voyez ces différences de caractères. Cependant, il pourrait ne pas être si propre avec aussi des problèmes plus complexes. Le filtre de bloom vient à l'esprit; il est très facile de savoir rapidement si l'élément n'est pas dans le jeu, mais difficile de dire si l'élément est dans le jeu. Alors que le même return technique pourrait s'appliquer, le code peut ne pas être aussi jolie.

21voto

hatchet Points 7251

Si vous regardez les implémentations de surcharges de == et != dans l' .net de source, ils ont souvent de ne pas mettre en œuvre != que !(gauche == droite). Ils le mettre pleinement en œuvre (comme ==) avec niée logique. Par exemple, DateTime implémente ==

return d1.InternalTicks == d2.InternalTicks;

et != comme

return d1.InternalTicks != d2.InternalTicks;

Si vous (ou le compilateur si il l'a fait implicitement) étaient à mettre en œuvre != comme

return !(d1==d2);

alors, vous faites une hypothèse au sujet de la mise en œuvre interne de == et != dans les choses que votre classe est le référencement. En évitant cette hypothèse peut être la philosophie derrière leur décision.

17voto

KeithS Points 36130

Pour répondre à vos modifier, au sujet de pourquoi vous êtes amené à remplacer à la fois si vous passer outre, c'est dans l'héritage.

Si vous remplacez ==, les plus susceptibles de fournir une sorte de sémantique ou de l'égalité structurelle (par exemple, DateTimes sont égales si leurs InternalTicks propriétés sont égaux même si elles peuvent être différentes instances de), vous sont en train de changer le comportement par défaut de l'exploitant de l'Objet, qui est le parent de tous .NET des objets. L'opérateur == est, en C#, une méthode, dont la base de la mise en œuvre de l'Objet.opérateur(==) effectue un référentiel de comparaison. Objet.opérateur(!=) est une autre méthode, différente, qui effectue également un référentiel de comparaison.

Dans presque tous les autres cas de redéfinition de méthode, il serait illogique de supposer qu'en substituant une méthode serait également entraîner un changement de comportement pour une antonymic méthode. Si vous avez créé une classe avec Increment() et de Décrémentation() les méthodes, et emportait Increment() dans une classe enfant, vous vous attendriez à ce Decrement() également être remplacé par l'inverse de votre substituée comportement? Le compilateur ne peut pas être assez intelligent pour générer une fonction inverse pour la mise en œuvre d'un opérateur dans tous les cas possibles.

Toutefois, les opérateurs, bien que mis en œuvre de façon très similaire aux méthodes, sur le plan conceptuel du travail en paires; == et !=, < et >, et <= et >=. Il serait illogique dans ce cas, du point de vue d'un consommateur à penser que != travaillé différemment ==. Ainsi, le compilateur ne peut pas être faite à supposer que l'!=b == !(a==b) dans tous les cas, mais il est généralement attendu que == et != devraient fonctionner dans un mode similaire, de sorte que le compilateur ne vous force à le mettre en paires, mais vous avez réellement final. Si, pour votre classe, un!=b == !(a==b), puis il suffit de mettre en œuvre la != opérateur à l'aide !(==), mais si cette règle ne tient pas dans tous les cas de votre objet (par exemple, si la comparaison avec une valeur particulière, de l'égalité ou de l'inégalité, n'est pas valide), alors vous devez être plus intelligents que les IDE.

La VRAIE question à se poser est pourquoi les < et>, et <= et >= sont des paires à des fins de comparaison les opérateurs qui doivent être mises en œuvre simultanément, lorsque, en termes numériques !(a < b) == a >= b et !(a > b) == a <= b. Vous devez être nécessaires pour mettre en œuvre tous les quatre si vous remplacez un, et vous devriez probablement être nécessaire de remplacer == (et !=) ainsi, parce que (a <= b) == (a == b) si a est sémantiquement égale à b.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X