【问题标题】:Custom string class equality自定义字符串类相等
【发布时间】:2013-07-22 03:19:57
【问题描述】:

我正在制作一个自定义字符串类。 (主要是出于自我教育的目的——我知道我不会想出比常规的 string 类更好的东西,而我不能像使用扩展方法一样轻松地做到这一点。)我遇到了一个奇怪的问题在我的单元测试中测试相等性。它几乎在所有方面都有效,除了一个。这是单元测试:

MyString myStr = "MyNewString";
Assert.AreEqual("MyNewString", myStr); //Fails
Assert.AreEqual(myStr, "MyNewString");
Assert.IsTrue(myStr.Equals("MyNewString"));
Assert.IsTrue(("MyNewString").Equals(myStr));
Assert.IsTrue(myStr == "MyNewString");
Assert.IsTrue("MyNewString" == myStr);

string realString = "MyNewString";
Assert.AreEqual(realString, myStr); //Fails
Assert.AreEqual(myStr, realString);
Assert.IsTrue(myStr.Equals(realString));
Assert.IsTrue(realString.Equals(myStr));
Assert.IsTrue(myStr == realString);
Assert.IsTrue(realString == myStr);

在这两种失败的情况下,如果我在myStr 之后添加.ToString(),它将成功,但在其他任何情况下都不需要这样做。我猜这是因为stringEquals 方法不知道我的课程,即使我已经设置了隐式转换。类的相关部分如下:

public struct MyString : ICloneable, IComparable<MyString>, IComparable<string>,
    IEnumerable<char>, IEquatable<MyString>, IEquatable<string>
{
    private char[] text;

    //Constructors
    ...

    public static implicit operator MyString(string s)
    {
        return s == null ? null : new MyString(s);
    }
    public static implicit operator string(MyString s) { return s.ToString(); }

    public static bool operator ==(MyString a, MyString b) { return a.Equals(b); }
    public static bool operator !=(MyString a, MyString b) { return !(a.Equals(b)); }
    public static bool operator ==(MyString a, string b) { return a.Equals(b); }
    public static bool operator !=(MyString a, string b) { return !(a.Equals(b)); }
    public static bool operator ==(string a, MyString b) { return b.Equals(a); }
    public static bool operator !=(string a, MyString b) { return !(b.Equals(a)); }

    public override string ToString() { return new string(text); }
    public override bool Equals(object obj)
    {
        if (obj == null)
            return false;
        if (base.Equals(obj))
            return true;
        if (obj is MyString)
            return this.Equals((MyString)obj);
        if (obj is string)
            return this.Equals((string)obj);
        return false;
    }
    public bool Equals(MyString other) { return this.CompareTo(other) == 0; }
    public bool Equals(string other) { return this.CompareTo(other) == 0; }
}

CompareTo() 也适用于 MyString 和常规 string,但请相信我,它们有效。我还能做些什么来使这个平等测试起作用吗?它似乎在除此之外的所有其他情况下都有效。我不确定Assert.AreEqual 在内部实际上是如何运作的,但如果其他所有测试相等性的方法都有效,为什么这个方法会失败?

编辑:添加我的 GetHashCode(),因为它可能是相关的:

public override int GetHashCode()
{
    int hash = 0;
    for (int i = 0; i < Length; i++ )
        hash ^= (i * this[i]);
     return hash;
}

当然这可能与string.GetHashCode() 不匹配,但我无法知道,因为我看不到他们的代码。 (我可以看到元数据,但它只包括标题而不包括实现。)尝试用string.GetHashCode() 的快捷方式替换它:

public override int GetHashCode()
{
    return new string(text).GetHashCode();
}

还是不行。还尝试添加扩展方法:

public static bool Equals(this string a, MyString b) { return b.Equals(a); }

那也没用。还有其他想法吗?

【问题讨论】:

  • 我非常非常非常好奇你为什么要这样做..
  • 一个字。实践。真正熟悉一门语言的最好方法就是总是编写一些东西。弄清楚如果所有花哨的类都不存在,你将如何做事。我不打算在任何生产代码中使用它。这只是为了我自己的提升。
  • 很公平.. 只是想我会问 :)

标签: c# string unit-testing compare equals


【解决方案1】:

调用栈提到了Assert.AreEqual(object, object)的调用 我假设这将调用object.Equals(object, object),它不会调用您的任何操作员。见http://blogs.msdn.com/b/csharpfaq/archive/2004/03/29/when-should-i-use-and-when-should-i-use-equals.aspx
BTW:请务必覆盖GetHashCode()

我不得不稍微扩展它。
Assert.AreEqual(a,b) 调用 object.Equals(a,b),后者调用 a.equals(b)。所以你的测试调用string.Equals(b)string.Equals 的实现为 ReferenceEqualsComparism 隐式转换为 String。因此,只要不存在对字符串的隐式强制转换,string.equality() 就会失败。
简而言之:从 MyString 到字符串的隐式强制转换不起作用 => 请参阅 In c# 3.0, is it possible to add implicit operators to the string class?

  var myStr = "hello" as MyString;   // does call implicit cast to MyString
  var y = myStr as string;           // does not call implicit cast to String becaus you are not owner of string

【讨论】:

  • 我确实覆盖了 GetHashCode()。为了简单起见,我把它省略了。现在我知道我的哈希函数匹配string 的哈希函数的几率非常小。匹配string 的哈希是让这种特殊的相等比较起作用的唯一方法吗? (有趣的是,当您反转操作数时它可以正常工作。)
  • 好吧,我只是用 return new String(text).GetHashCode(); 替换了我的 GetHashCode()(比试图猜测 string 的实际哈希函数更容易),但在这两种情况下它仍然失败,所以这是不是。
  • 那么最终的结果就是根本无法做到吗?我根本无法更改Assert.AreEqualobject.Equalsstring.Equals。我什至不能扩展和覆盖字符串类,因为它是sealed。 (密封类在 IMO 中有点生涩。)在这种情况下,甚至扩展方法都被忽略了,这是否意味着我只是不走运?如果是这种情况,Assert.IsTrue(realString.Equals(myStr)); 是如何工作的?那不也只是打电话给string.Equals吗?
  • 您的评论包含非常好的观点。 Its interesting to see:&lt;br/&gt;String y = myStr;` 工作,而 String z = myStr as string; 不编译。因此,您的示例在参数调用期间通过直接赋值调用隐式运算符,而 as-operator 失败。
    我认为没有进一步的可能性让您的代码运行,因为 '=' 和 'as' 不能重载。跨度>
猜你喜欢
  • 2019-06-02
  • 1970-01-01
  • 2011-02-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-20
相关资源
最近更新 更多