【问题标题】:Implicit cast operator and the equality operator隐式强制转换运算符和相等运算符
【发布时间】:2012-03-23 09:58:55
【问题描述】:

假设我有一个支持隐式转换为 System.String 的简单对象

public sealed class CompanyCode
{
    public CompanyCode(String value)
    {
        { Regex validation on value format }
        _value = value;
    }

    readonly String _value;

    public override String ToString() => _value;

    static public implicit operator String(CompanyCode code) =>
        code?.ToString();
}

现在让我们在程序的另一部分中说我与一个字符串进行比较:

var companyCode = { some company code object }

if (companyCode == "MSFTUKCAMBS")
    // do something...

编译器对== 运算符做了什么?是否将 companyCode 隐式转换为字符串并运行 System.String == 实现?是否使用System.Object == 实现?或者编译器会抱怨我吗? (我现在没有编译器来检查这个)。

据我所知,我还有其他几个选择。

  • 在 CompanyCode 上实现 ==(String x) 运算符。
  • 在 CompanyCode 上实现 IEquatable<String> 接口。

这些选项中的任何一个(或两个)会更可取吗?

【问题讨论】:

    标签: c# operator-overloading implicit-conversion


    【解决方案1】:

    假设我有一个支持隐式转换为 System.String 的简单对象

    我会质疑这个设计决定。事实上,它提出了运算符重载的问题,这表明您的同事会问同样的问题。我什至不知道答案(就编译器会做什么而言)。

    我绝对不会建议实现IEquatable<string>,因为那样x.Equals(y) 将不会与y.Equals(x) 对称。你可以在你的类中实现==的两个重载,双向循环......但它与Equals不一致。

    我建议只拥有一个名为 ValueCode 的字符串类型的属性,然后您可以使用:

    if (companyCode.Value == "MSFTUKCAMBS")
    

    这意味着什么会立即清楚。

    基本上,我认为适合隐式转换的情况很少见。

    来自Design Guidelines for Class Library Developers

    如果最终用户没有明确期望这种转​​换,则不要提供转换运算符。

    这里有这么明确的期待吗?

    【讨论】:

    • +1 表示隐式转换重载的警告。我想在“明确预期”的基础上添加隐式转换应该是无损的并且不需要转换。它也不应该引发任何异常。
    • 我很欣赏你对运营商的看法,我可能有点过度设计了。我会告诉你为什么我不愿意公开 _value 字段——这个对象的目的是一个简单的字符串值与一些逻辑(例如正则表达式验证)绑定在一起。通过公开 _value 字段,我基本上允许人们将值从逻辑中分离出来,失去了将它们捆绑在一起的好处。在现有的代码库中,这些公司代码作为字符串传递,并且验证(&其他)逻辑大量重复。我很想阻止这种情况。
    • @MattDavey:我并不是建议您公开您尚未公开的任何内容:您甚至不需要额外的属性,因为客户可以调用ToString()。我当然不是建议让它可写
    • @JonSkeet 我担心的是人们将值与行为分离(通过暴露的 _value 或 ToString)调用堆栈的高处,然后将原始值向下传递 - 导致我现在处于重复行为的相同情况。有什么方法可以确保行为无论走到哪里都遵循值,同时仍然允许字符串比较?
    【解决方案2】:

    它将隐式转换为字符串并使用字符串的 == 运算符检查相等性。 对于你展示的案例——你提供的每一种方式都是合适的,但每一种方式都有不同的目的和意义。

    通常应避免隐式转换。 实现 == 是为了允许与字符串进行比较, 而 IEquatable 只是允许将类用作 IEquatable 类型,用于外部代码引用。 IEquatable 很可能只返回 == 结果。

    对于这种情况,我会选择 == 运算符重载,除非您有任何其他使用隐式转换的目的。

    另外,如果你使用隐式转换,重载显式转换而不是隐式转换在代码方面会有点丑陋,但在健壮方面会更聪明,所以每当有人想将你的类转换为字符串时,他必须使用 (string)obj 进行转换,这也是代码中真正发生的事情的一个很好的提醒。

    【讨论】:

    • 几年后重新审视这个问题,我现在认为这是最好的答案。众所周知,隐式转换应该是无损的。我在对 Jons 的回答中提到我希望类行为始终与字符串值相关联。因此,隐式转换为字符串实际上是有损的。使转换显式使代码更清晰。
    【解决方案3】:

    我个人建议在这些情况中不要使用operator overloading。看代码有点令人困惑,要理解发生了什么。

    这是一个更好的,imo,有一些功能,特别是显示比较操作,或者像乔恩建议的那样,使用一个属性。

    简而言之,让您的代码的读者清楚地了解您要比较的内容。

    【讨论】:

    • 嗨蒂格兰。当您说一个显示比较的函数时 - 您是指一个公开比较委托的属性,其中一个操作数已被柯里化到内部 _value 字段?
    • @MattDavey:我的意思是(说)IComparable 实现并调用比较,所以我看到它调用比较和 intent 来查看一些自定义逻辑(不是 当我看到 ==) 时打算
    • 是的,我认为这与 Jon 建议反对的 IEquatable 实现基本相同。 IComparable 是否比 IEquatable 更可取?
    • @MattDavey:不,没有特别的原因,如果不是架构。你可以选择你想要的,重要的是做一个自定义比较你让读者特别清楚,这不是使用 == 运算符重载。
    • @MattDavey:我的想法:这是 CompanyCode 类的行为。 CompanyCode 是一个自定义类,因此应明确说明要比较的内容和方式。
    猜你喜欢
    • 2013-08-25
    • 2010-10-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多