C# 隐式强制转换运算符和相等运算符

C# 隐式强制转换运算符和相等运算符,c#,operator-overloading,implicit-conversion,C#,Operator Overloading,Implicit Conversion,假设我有一个简单的对象,它支持对System.String进行隐式转换 public sealed class CompanyCode { public CompanyCode(String value) { { Regex validation on value format } _value = value; } readonly String _value; public override String ToStr

假设我有一个简单的对象,它支持对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上实现
    ==(字符串x)
    运算符
  • 在CompanyCode上实现
    IEquatable
    接口
这些选项中有哪一个(或两者)更可取

假设我有一个简单的对象,它支持对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();
}
首先,我会质疑这个设计决定。它提出了操作员过载的问题,这表明你的同事也会问同样的问题。我甚至都不知道答案(就编译器将做什么而言)

我绝对不会建议实现
IEquatable
,因为这样
x.Equals(y)
就不会与
y.Equals(x)
对称。您可以在类中实现两个
=
重载,两种方式都可以。。。但是它与
等于
不一致

我建议只使用字符串类型的名为
Value
code
的属性,然后您可以使用:

if (companyCode.Value == "MSFTUKCAMBS")
这意味着什么,我们马上就会明白

基本上,我认为隐式转换适用的情况非常少

如果最终用户未明确预期此类转换,则不要提供转换运算符


这里有这样一个明确的期望吗?

我个人建议在这些情况下不要使用
操作符重载。查看代码,理解正在发生的事情有点让人困惑

在我看来,这是一个更好的方法,它有一些函数专门表示比较操作,或者像Jon建议的那样,使用一个属性


简而言之,让代码的读者清楚地知道要比较什么

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

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

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


此外,如果您使用隐式转换,那么在代码方面,重载显式转换(而不是隐式转换)会有点丑陋,但在健壮性方面,它会更聪明。因此,每当您想要将类转换为字符串时,就必须使用(string)obj强制转换它,这也很好地提醒了我们代码中到底发生了什么。

+1提示隐式强制转换重载。我想在“明确预期”的基础上补充一点,即隐式转换应该是无损的,不需要转换。这也不应该引起任何例外。我理解你关于操作员的观点,我很可能有点过度设计了。我将告诉您为什么我不愿意公开_value字段——这个对象的目的是将一个简单的字符串值与逻辑位(例如regex验证)绑定在一起。通过公开_value字段,我基本上允许人们将值从逻辑中分离出来,失去了将它们绑定在一起的好处。在现有的代码库中,这些公司代码作为字符串传递,并且验证(其他)逻辑被大量复制。我很想阻止它。@MattDavey:我不是建议你公开任何你还没有公开的东西:你甚至不需要额外的属性,因为客户端可以调用
ToString()
。我当然不是建议让它可写。@JonSkeet我担心的是,人们在调用堆栈的某个较高位置将值从行为中分离出来(通过暴露的_值或ToString),然后将原始值向下传递,这会导致与我现在所处的情况相同的行为被复制的情况。有没有什么方法可以确保在允许字符串比较的情况下,行为在任何地方都遵循值?嗨,Tigran。当你说一个显示比较的函数时——你是说一个属性公开了一个比较委托,其中一个操作数被转换到了内部的_值字段吗?@MattDavey:我的意思是(比如)IComparable实现和调用compare,所以我看到它调用compare和intent来查看一些自定义逻辑(当我看到==时,这不是我想要的).是的,我认为这与Jon推荐的IEquatable实现基本相同。有没有具体原因表明IComparable比IEquatable更可取?@MattDavey:没有,如果不是架构上的,也没有特别的原因。你可以选择你想要的,重要的是与读者espl进行自定义比较非常清楚,这不是使用==运算符重载。@MattDavey:我的想法是:这是CompanyCode类的一种行为。CompanyCode是一个自定义类,所以应该明确它想要比较什么以及如何比较。几年后再访问这个问题,我现在认为这是最好的答案。正如我们所知,隐式转换应该是无损的。我在对Jons回答的评论中提到,我希望类行为始终与字符串值相关联。因此,对字符串的隐式转换实际上是有损的