我实现了一个名为NonEmptyString的类,当它不为空时不允许创建.我让这个类实现了IEquatable<NonEmptyString>IEquatable<string>.我有Equals(object obj)Equals(NonEmptyString other)Equals(string other)GetHashCode()的覆盖.然后我编写了一些测试,发现几乎所有东西都能正常工作.除了一种情况外,调用静态equals方法时,字符串参数是第一个参数.见this line here.

string text = "ASDF123";
NonEmptyString nonEmptyString = NonEmptyString.CreateUnsafe("ASDF123");
Assert.True(text == nonEmptyString);
Assert.True(nonEmptyString == text);
Assert.True(text.Equals(nonEmptyString)); // This one returns true as expected.
Assert.True(nonEmptyString.Equals(text));
Assert.True(Equals(text, nonEmptyString)); //This is the only one that doesn't work.
Assert.True(Equals(nonEmptyString, text));

我想知道为什么会这样--当我查看对象上的equals方法的实现时,它确实调用了虚拟的Equals(object obj)方法.因此,如果该方法返回FALSE,那么我预计只有text.Equals(nonEmptyString)会发生同样的情况--但这一种方法是有效的.这是我进入调用时看到的静态equals的实现.

public static bool Equals(object? objA, object? objB)
{
    if (objA == objB)
    {
        return true;
    }
    if (objA == null || objB == null)
    {
        return false;
    }
    return objA.Equals(objB);
}

我甚至try 用这种方式重写用于比较字符串和非EmptyString的==个运算符(我真的没想到会有帮助,但值得一试)

public static bool operator ==(string obj1, NonEmptyString obj2)
public static bool operator !=(string obj1, NonEmptyString obj2)
public static bool operator ==(NonEmptyString obj1, string  obj2)
public static bool operator !=(NonEmptyString obj1, string obj2)

我能做些什么让这件事奏效吗? 预计这不会奏效吗? 它是.NET中的错误吗?

以下是核心实现(我go 掉了其中不重要的部分).

public sealed class NonEmptyString : IEquatable<string>, IEquatable<NonEmptyString>
{
    private NonEmptyString(string value)
    {
        Value = value;
    }

    public string Value { get; }

    public static NonEmptyString CreateUnsafe(string value)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            throw new ArgumentException("You cannot create NonEmptyString from whitespace, empty string or null.");
        }

        return new NonEmptyString(value);
    }

    public override int GetHashCode()
    {
        return Value.GetHashCode();
    }

    public override bool Equals(object obj)
    {
        return ReferenceEquals(this, obj) ||
               obj is NonEmptyString otherNonEmpty && Equals(otherNonEmpty) ||
               obj is string otherString && Equals(otherString);
    }

    public bool Equals(string other)
    {
        return Value.Equals(other);
    }

    public bool Equals(NonEmptyString other)
    {
        return Value.Equals(other?.Value);
    }

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

推荐答案

您似乎遇到的问题是,当您从stringobject类调用Equals重载时.

请看下面的代码:

string text = "ASDF123";
NonEmptyString nonEmptyString = NonEmptyString.CreateUnsafe(text);
/* 3 */ Assert.True(text.Equals(nonEmptyString));
/* 5 */ Assert.True(Equals(text, nonEmptyString));

在第3行,对Equals的调用是在string实例上进行的,该实例对NonEmptyString类一无所知--因此,无论NonEmptyString的基础值是否相等,它都将始终返回false.

在第5行,对Equals的调用是在object实例上进行的,同样,它对NonEmptyString类一无所知-因此,无论NonEmptyString的基础值是否相等,它都将始终返回false.

以下是代码的编译器优化版本:

NonEmptyString nonEmptyString = NonEmptyString.CreateUnsafe("ASDF123");
Assert.True("ASDF123".Equals(nonEmptyString));
Assert.True(object.Equals("ASDF123", nonEmptyString));

你无法控制这Equals个重载.


为了使您的工作尽可能简单,您应该实现==个隐式和显式转换操作符,如下所示:

public static bool operator ==(string obj1, NonEmptyString obj2) => obj2.Equals(obj1);
public static bool operator !=(string obj1, NonEmptyString obj2) => !obj2.Equals(obj1);
public static bool operator ==(NonEmptyString obj1, string obj2) => obj1.Equals(obj2);
public static bool operator !=(NonEmptyString obj1, string obj2) => !obj1.Equals(obj2);

public static implicit operator string(NonEmptyString nes) => nes.Value;
public static explicit operator NonEmptyString(string text) => NonEmptyString.CreateUnsafe(text);

Csharp相关问答推荐

更新数据库中的对象失败,原因是:Microsoft. EntityFrame Core. GbUpdateConcurrencyResponse'

Serilog SQL服务器接收器使用UTC作为时间戳

如何使用Unity和MRTK3将手网添加到Hololens 2应用程序中

ASP.NET MVC中创建视图的过滤器

WPF DataGrid中的三维数据

JsonSerializer.Deserialize<;TValue>;(String,JsonSerializerOptions)何时返回空?

MS Graph v5.42.0:在寻呼消息时更改页面大小

反序列化私有成员

DateTime ToString()未以指定格式打印

依赖项注入、工厂方法和处置困境

如何使用用于VS代码的.NET Maui扩展在我的iOS/Android设备或模拟器上进行调试?

为什么我的用户界面对象移动到略低于实际目标?

如何使用EPPlus C#在单个单元格中可视化显示多行文字

VS代码扩展无法在新版本扩展C#中运行从v2.10.28开始

如何阻止可传递依赖项出现在项目中

如何为控制器PUT操作绑定对象数组

与Visual Studio 2022中的.NET框架相比,如何在.NET Core 6中获取错误输出的窗口句柄

XmlSerializer在遇到XML属性(命名空间)时崩溃

使用c#中的Windows 10关机消息

更新实体框架上的被跟踪实体