Question

J'ai défini une classe C # avec un membre de la chaîne. À toutes fins un, pensent de cette classe comme étant une sous-classe de chaîne (sauf qui n'est pas autorisé). Je l'utilise pour représenter un champ de chaîne fortement typé qui correspond à un format spécifique (j'ai simplifié ce qui a beaucoup).

public class field
{
    private readonly string m_field;
    public field(string init_value)
    {
        //Check the syntax for errors
        if (CheckSyntax(init_value))
        {
            m_field = init_value;
        }
        else
        {
            throw new ArgumentOutOfRangeException();
        }
    }

    public override string ToString()
    {
        return m_field;
    }
}

Maintenant, je veux être en mesure de comparer cette classe directement à une autre chaîne (objet ou littérale). Par conséquent, je mis à exécution les suivant dans la classe:

public override bool Equals(object obj)
{
    if (obj == null)
    {
        return false;
    }

    return this.m_field == obj.ToString();
}

public override int GetHashCode()
{
    return this.m_field.GetHashCode();
}

public static bool operator ==(field x, Object y)
{
    if ((object)x == null && y == null)
    {
        return true;
    }
    else if ((object)x == null || y == null)
    {
        return false;
    }
    else
    {
        return (x.m_field == y.ToString());
    }
}

public static bool operator !=(field x, Object y)
{
    return !(x == y);
}

Maintenant, quand j'écris un test unitaire, en fonction de l'ordre que je passe dans les arguments à Assert.AreEqual, je reçois des résultats différents:

string valid = "Some String";
field target = new field(valid);
Assert.AreEqual(target, valid); // PASSES
Assert.AreEqual(valid, target); // FAILS

Je suppose que c'est parce que dans le premier assert, qu'il appelle field.Equals () et dans le second qu'il appelle String.Equals (). De toute évidence, je suis approche de ce mauvais angle. Quelqu'un peut-il me donner un aperçu?

Une autre chose. Je ne peux pas utiliser struct ici (type de valeur), car dans mon cas réel je définir tout cela dans une classe de base et héritant de lui.

Était-ce utile?

La solution

Ceci est décrit en détail dans efficace Java comme point 8:. Obéissez le contrat général lors de la substitution equals

  

La méthode de equals met en oeuvre une relation d'équivalence.

Il est réflexif, Symétrique, transitive, cohérente, et pour tout x de référence non nul, x.equals(null) doit retourner false. L'exemple cité pour briser la symétrie est semblable à la vôtre.

classe field est au courant de classe string, mais la classe string intégré ne connaît pas field. Cette une interopérabilité à sens unique, et devrait être supprimée.

Autres conseils

Fondamentalement, vous ne pouvez pas faire ce que vous voulez - il n'y a aucun moyen que vous pouvez faire string reconnaître votre classe à des fins d'égalité. Vous ne serez jamais en mesure de le faire réflexif -. Vous ne serez jamais en mesure de le faire obéir au contrat de object.Equals

Je voudrais essayer personnellement de le refaire de sorte que vous n'avez pas eu la validation dans le cadre du type lui-même -. Rendre une partie des propriétés pertinentes des entités commerciales (ou tout ce qu'ils sont)

Je décourager toute personne qui utilise votre classe de champ implicitement comme une chaîne, et la force de ce type d'utilisation:

string valid = "Some String";
field target = new field(valid);
Assert.AreEqual(target.toString(), valid); 
Assert.AreEqual(valid, target.toString());

D'après les commentaires de tout le monde, et mes propres besoins, voici ce que je présente comme une solution possible (je modifier la méthode Equals comme suit):

public override bool Equals(Object obj)
{
    if (obj == null)
    {
        return false;
    }

    field f = obj as field;
    if (f != null)
    {
        return this == f;
    }
    else
    {
        return obj.Equals(this);
    }
}

Cela semble permettre son utilisation correcte dans les classes de dictionnaire et de collecte qui se fondent sur les méthodes equals et GetHashCode pour déterminer si la valeur existe déjà.

En outre, maintenant ces deux échouent:

string valid = "Some String";
field target = new field(valid);
Assert.AreEqual(target, valid); // FAILS
Assert.AreEqual(valid, target); // FAILS

Et ces deux passent:

string valid = "Some String";
field target = new field(valid);
Assert.AreEqual(target.ToString(), valid); // PASSES
Assert.AreEqual(valid, target.ToString()); // PASSES

Et ces deux passent:

field f1 = new field("Some String");
field f2 = new field("Some String");
Assert.AreEqual(f1, f2); // PASSES
Assert.AreEqual(f2, f1); // PASSES

est une chaîne # Equals

public override bool Equals(object obj)
{
    string strB = obj as string;
    if ((strB == null) && (this != null))
    {
        return false;
    }
    return EqualsHelper(this, strB);
}

Fournir un argument autre qu'une chaîne à cordes # Equals va revenir faux. Je suggère un « repenser » pour contourner cela.

Je suggère d'utiliser object.ReferenceEquals () si vous essayez de valider en interne si x ou y est nulle.

public static bool operator ==(field x, Object y)
{
    if (object.ReferenceEquals(x, null) && object.ReferenceEquals(y, null))
    {
        return true;
    }
    else if (object.ReferenceEquals(x, null) || object.ReferenceEquals(y, null))
    {
        return false;
    }
    else
    {
        return (x.m_field == y.ToString());
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top