C# 重写等于并与字符串进行比较

C# 重写等于并与字符串进行比较,c#,equals,C#,Equals,我定义了一个带有字符串成员的C#类。出于所有目的,将此类视为string的子类(不允许的情况除外)。我用它来表示一个与特定格式匹配的强类型字符串字段(我已经大大简化了它) 现在,我希望能够将这个类直接与任何其他字符串(对象或文本)进行比较。因此,我在课堂上实现了以下内容: public override bool Equals(object obj) { if (obj == null) { return false; } return this

我定义了一个带有字符串成员的C#类。出于所有目的,将此类视为string的子类(不允许的情况除外)。我用它来表示一个与特定格式匹配的强类型字符串字段(我已经大大简化了它)

现在,我希望能够将这个类直接与任何其他字符串(对象或文本)进行比较。因此,我在课堂上实现了以下内容:

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);
}
现在,当我编写单元测试时,根据我将参数传递给Assert.AreEqual的顺序,我会得到不同的结果:

string valid = "Some String";
field target = new field(valid);
Assert.AreEqual(target, valid); // PASSES
Assert.AreEqual(valid, target); // FAILS
我假设这是因为在第一个断言中,它调用field.Equals(),在第二个断言中,它调用String.Equals()。显然,我从错误的角度来看待这个问题。有人能给我一些见解吗


还有一件事。我不能在这里使用结构(值类型),因为在我的实际情况中,我是在基类中定义所有这些内容并从中继承的。

基本上你不能做你想做的事情-你无法让
字符串
为了平等的目的识别你的类。你永远无法使它具有反身性——你永远无法使它遵守
object.Equals
的约定


我个人会尝试重新设计它,这样您就不会将验证作为类型本身的一部分—使其成为业务实体(或其他实体)的相关属性的一部分。

我不鼓励任何人将您的字段类隐式用作字符串,并强制使用这种类型:

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

这在as第8项中有详细说明:当覆盖
等于
时,遵守总合同

equals
方法实现了一个等价关系

它是自反的、对称的、传递的、一致的,对于任何非空引用x,
x.equals(null)
必须返回
false
。引用的打破对称的例子与你的相似


字段
类知道
字符串
类,但内置的
字符串
类不知道
字段
。这是一种单向互操作性,应该删除。

根据每个人的反馈和我自己的需要,以下是我提出的可能解决方案(我正在修改Equals方法,如下所示):

这似乎允许在依赖Equals和GetHashCode方法确定值是否已经存在的字典和集合类中正确使用它

而且,现在这两项都失败了:

string valid = "Some String";
field target = new field(valid);
Assert.AreEqual(target, valid); // FAILS
Assert.AreEqual(valid, target); // FAILS
这两项都通过了:

string valid = "Some String";
field target = new field(valid);
Assert.AreEqual(target.ToString(), valid); // PASSES
Assert.AreEqual(valid, target.ToString()); // PASSES
field f1 = new field("Some String");
field f2 = new field("Some String");
Assert.AreEqual(f1, f2); // PASSES
Assert.AreEqual(f2, f1); // PASSES
这两项都通过了:

string valid = "Some String";
field target = new field(valid);
Assert.AreEqual(target.ToString(), valid); // PASSES
Assert.AreEqual(valid, target.ToString()); // PASSES
field f1 = new field("Some String");
field f2 = new field("Some String");
Assert.AreEqual(f1, f2); // PASSES
Assert.AreEqual(f2, f1); // PASSES
这是字符串#等于


为String#Equals提供字符串以外的参数将返回false。我建议“重新思考”来解决这个问题。

如果您在内部尝试验证x或y是否为null,我建议使用object.ReferenceEquals()

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());
    }
}

+1像这样的类型很难使用,也很难让其他人始终如一地使用。最好在实体本身中保留验证和其他业务逻辑。不幸的是,我在字典和集合等中使用此类实例作为键。我需要等量()和GethAsHeCuffE(),以类似于值类型,使它们在这些情况下正确工作。重写均衡器(),并且您可能想考虑实现ICM,我真的希望MS将运算符重载出来,C。实际上,如果我可以从字符串继承并添加自己的构造函数,我就不会有这个问题。@Scott:是的,你仍然会有同样的问题。“foo”.Equals(instanceOfYourType)仍然不会返回true。相等和继承从根本上说是一个棘手的问题。这或多或少是默认的Equals运算符,因此不重写它将提供等效的功能。由于字段表示文本数据,因此可以使用“returnthis.text==f.text”。但是在这种情况下,您需要重写GetHashCode以返回这样的.text.GetHashCode(),这将确保集合类的行为良好。是的,如果您查看原始问题,它会重写GetHashCode()。这不起作用。。。canonical Equals实现有一个如下检查。GetType()!=arg.GetType()返回false。所以做一个“value.Equals(fieldObj)”肯定会失败。没有办法解决它。@Gishu-是的,这是有意的。只要“value”.Equals(fieldObj)和fieldObj.Equals(“value”)都返回相同的内容,就可以了。然后我可以使用“value.Equals”(fieldObj.ToString())。没问题。另一方面,为什么(this!=null)-在实例方法中不能保证它是非null的?
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());
    }
}