C#,多个 == 运算符重载,没有模棱两可的空值检查

Posted

技术标签:

【中文标题】C#,多个 == 运算符重载,没有模棱两可的空值检查【英文标题】:C#, multiple == operator overloads without ambiguous null check 【发布时间】:2017-07-10 22:28:39 【问题描述】:

简介: 我有几个类做同样的工作,但具有不同的值类型(例如浮点数或整数的向量)。 现在我希望能够检查相等性,这种相等性也应该在类型之间起作用(例如 vectorF == vectorI)。 此外,应该可以进行空检查(vectorF == null)。

方法: 我的方法是为 == 和 != 运算符创建多个重载,每个可能的组合一个。

public sealed class VectorF

    [...]

    public static bool operator == (VectorF left, VectorI right)
    
        // Implementation...
    

    public static bool operator == (VectorF left, VectorF right)
    
        // Implementation...
    

    // Same for != operator
    [...]

问题: 使用多个重载,我不能只使用 == 运算符进行空检查,因为调用会模棱两可。

var v = new VectorF([...]);

if (v == null)    // This call is ambiguous
[...]

我知道可以使用 ReferenceEquals 或 null 强制转换,但这种方法对我来说是一个严重的限制。

var v = new VectorF([...]);

if(object.ReferenceEquals(v, null))    // Would work, is not user friendly.
[...]

if(v == (VectorF)null)    // Would also work, is neither user friendly.
[...]

问题: 有没有办法以某种方式实现 == 运算符,它允许简单的空值检查,并允许不同向量之间的相等性检查?

或者,我可以/应该如何实现这一点?

【问题讨论】:

看起来你的 Vector 类应该是不可变的结构。那么null 的问题就不会出现。 (我假设您的类只包含 2 或 3 个值。) 听起来像zugzwang:要么好看==要么== null。如何有方法与其他类型的向量进行比较?例如。 VectorF.IsSame(VectorI) ?编译器在尝试vectorF == vectorI 时会显示用户错误,然后用户将搜索方法进行比较,tada,问题解决了吗?另一件事:比较floatint 并不精确,如何先将VectorF 转换为VectorI,然后比较两个VectorI 为什么你需要左右重载..你能分享我们重载方法的内容吗.. 我认为添加从 VectorI 到 VectorF 的隐式转换可以简化很多问题 @levent:我只是习惯于调用左右变量,我已经从其他重载中采用了这个,其中顺序可能很重要(例如矩阵乘法)。 【参考方案1】:

我会从一开始就推迟整个设计。我永远不会使用不同类型之间的值语义来实现 ==,我会觉得它相当混乱:instaceTypedA == instanceTypedB 大喊引用相等(至少对我而言)。

如果您需要此功能,请在 VectorIVectorF 之间实现隐式转换。这就是框架的工作方式。当您执行以下操作时:

int i = 1;
double d = 1;
var b = i == d;

超载==(int, double) 不会神奇地产生。发生的情况是i 被隐式转换为double 并调用==(double, double)

【讨论】:

谢谢。你是对的,== 应该是引用相等。我没有想到隐式转换,因为我希望调用尽可能优化。但是,由于无论如何都会转换值,所以它毕竟没有太大区别。【参考方案2】:

您可以通过使用is来反转比较:

if (v is VectorF)

如果vnull,此检查将失败。

【讨论】:

我不太喜欢这个解决方案。你真正在做的事情的语义隐藏在一个“聪明”的把戏中;阅读此代码,说实话,您是否正在检查 null 远不清楚。 嗯,使用is(或as)进行类型检查是很常见的,尽管不是每个人都知道它也会进行空值检查。一旦你知道很明显null 不是特定类型。 对 object.ReferenceEquals 的改进是什么,除了更短的代码?我会假设 is 对空值检查有相当大的性能开销。 我猜差别不大,但你可以测试一下。【参考方案3】:

在这种情况下,我会做的是不要重载 == 运算符,而是执行以下操作:

public static bool operator == (VectorF left, object right) 
    if (object.ReferenceEquals(null, right)) 
        // handle null case
    
    VectorF rightF = right as VectorF;
    if (!object.ReferenceEquals(null, rightF)) 
        // Compare VectorF
    
    VectorI rightI = right as VectorI;
    if (!object.ReferenceEquals(null, rightI)) 
        // Compare VectorI
    
    // and so on...

【讨论】:

Ewww,牺牲编译时类型安全来节省重复空检查?

以上是关于C#,多个 == 运算符重载,没有模棱两可的空值检查的主要内容,如果未能解决你的问题,请参考以下文章

C#中泛型参数的空值或默认比较

std :: optional的模棱两可的运算符重载?

“运算符”的 C++ 模棱两可的重载

在运行时出现此错误“用于空值的空检查运算符”

处理数据网格中的空值

用于空值的空值检查运算符