我已经定义了一个带有字符串成员的C#类。对于所有意图而言,将此类视为字符串的子类(除非不允许)。我用它代表一个匹配特定格式的强类型字符串字段(我已经大大简化了这一点)。
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;
}
}
现在,我希望能够将此类直接与任何其他字符串(对象或文字)进行比较。因此,我在课堂上实现了以下内容:
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()。显然我是从错误的角度接近这个。谁能给我一些见解?
另一件事。我不能在这里使用结构(值类型),因为在我的实际情况中,我在基类中定义所有这些并继承它。
答案 0 :(得分:9)
基本上你不能做你想做的事 - 你无法让string
认出你的班级是为了平等。你永远无法使它反身 - 你永远无法使它服从object.Equals
的契约。
我个人会尝试重新设计它,以便您没有将验证作为类型本身的一部分 - 使其成为业务实体的相关属性(或其他任何属性)的一部分。
答案 1 :(得分:5)
这在Effective Java中详细描述为第8项:在覆盖equals
时遵守一般合同。
equals
方法实现了等价关系。
它是Reflexive,Symmetric,Transitive,Consistent,对于任何非空引用x,x.equals(null)
必须返回false
。打破对称性的例子与你的相似。
field
类知道string
类,但内置string
类不知道field
。这是一种单向互操作性,应该被删除。
答案 2 :(得分:4)
我会阻止任何人将您的字段类隐式地用作String,并强制使用此类用法:
string valid = "Some String";
field target = new field(valid);
Assert.AreEqual(target.toString(), valid);
Assert.AreEqual(valid, target.toString());
答案 3 :(得分:0)
基于每个人的反馈和我自己的需求,这是我提出的可能的解决方案(我正在修改Equals方法如下):
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);
}
}
这似乎允许它在依赖于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
答案 4 :(得分:0)
这是String#Equals
public override bool Equals(object obj)
{
string strB = obj as string;
if ((strB == null) && (this != null))
{
return false;
}
return EqualsHelper(this, strB);
}
将String以外的参数提供给String#Equals将返回false。我建议“重新思考”以解决这个问题。
答案 5 :(得分:0)
如果您在内部尝试验证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());
}
}