4

我已经定义了一个带有字符串成员的 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()。显然我从错误的角度来处理这个问题。谁能给我一些见解?

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

4

6 回答 6

9

基本上你不能做你想做的事——你不可能string为了平等的目的而承认你的班级。您将永远无法使其具有自反性-您将永远无法使其遵守object.Equals.

我个人会尝试重新设计它,以便您没有将验证作为类型本身的一部分 - 使其成为业务实体(或任何它们)的相关属性的一部分。

于 2009-04-27T00:28:42.107 回答
5

这在Effective Java中作为第 8 条进行了详细描述:在覆盖时遵守一般合同equals

equals方法实现了等价关系。

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

fieldclass 知道stringclass,但内置string类不知道field. 这是一种单向互操作性,应该删除。

于 2009-04-27T00:35:05.897 回答
4

我会劝阻任何人将您的字段类隐式用作字符串,并强制使用这种类型的用法:

string valid = "Some String";
field target = new field(valid);
Assert.AreEqual(target.toString(), valid); 
Assert.AreEqual(valid, target.toString());
于 2009-04-27T00:29:27.657 回答
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
于 2009-04-27T00:57:58.947 回答
0

这是字符串#Equals

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

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

于 2009-04-27T06:02:43.410 回答
-1

如果您在内部尝试验证 x 或 y 是否为空,我建议使用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());
    }
}
于 2013-03-18T17:09:09.350 回答