C#中如何实现多个==运算符重载且不产生歧义的空值检查。

18

介绍:
我有几个类,它们完成相同的工作,但使用不同的值类型(例如浮点数或整数的向量)。
现在我想要能够检查它们是否相等,这种相等性也应该适用于类型之间的比较(例如 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.
[...]

问题:
有没有一种方法可以实现==运算符,使其允许简单的空值检查,并允许在不同向量之间进行相等比较?

或者,我还能/应该以另一种方式实现这个吗?


5
看起来你的向量类应该是不可变的结构体。这样就不会出现“null”的问题。(我假设你的类只包含2或3个值。) - Matthew Watson
2
听起来像是卒子局面:要么好看==或者== null。那么,如何使用其他类型的向量进行比较呢?例如,VectorF.IsSame(VectorI)?当用户尝试vectorF == vectorI时,编译器将显示用户错误,然后用户将搜索比较方法,问题解决了?另一件事:将floatint进行比较不会很精确,如何先将VectorF转换为VectorI,然后再比较两个VectorI - Sinatr
为什么需要左右重载?你能分享一下重载方法的内容吗? - levent
1
我认为你可以通过从VectorI到VectorF添加隐式转换来简化很多问题。 - Steve
@levent:我习惯于将变量称为left和right,这是我从其他重载中采用的,其中顺序可能很重要(例如矩阵乘法)。 - Chillersanim
@Sinatr:你使用另一种方法的想法听起来不错,但只有在从VectorI转换为VectorF时存在相对较大的开销时才适用。正如InBetween在他的回答中所描述的那样,值转换仍然会发生。如果Vector例如由数组支持,我认为你的方法是可行的。 - Chillersanim
3个回答

20

我本来就对整个设计有所保留。我不会在不同类型之间使用值语义来实现==,因为我会感到相当困惑:instanceTypedA == instanceTypedB 看起来像是引用相等(至少对我来说是这样)。

如果你需要让它工作,那么请在VectorIVectorF之间实现隐式转换。这就是框架的工作原理。当你执行以下操作时:

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

一个 ==(int, double) 的重载不会自动生成。实际上发生的是 i 隐式转换为 double 并调用 ==(double, double)


谢谢。你是正确的,应该使用引用相等性(reference equality)的 == 运算符。我没有考虑到隐式转换,因为我希望该调用尽可能地优化。然而,由于值最终会被转换,所以事实上并没有太大区别。 - Chillersanim

7
您可以使用is来进行比较的反转:
if (v is VectorF)

如果vnull,则此检查将失败。


6
我不太喜欢这个解决方案。你真正做的语义是隐藏在一个“巧妙”的技巧中;老实说,阅读这段代码,很难看出你是否在检查空值。 - InBetween
1
尽管并非每个人都知道它也可以进行空检查,但使用“is”(或“as”)进行类型检查非常普遍。一旦你知道了这一点,就很明显null不属于特定类型。 - Patrick Hofman
除了代码更短之外,object.ReferenceEquals有什么改进吗?我认为is在仅进行空检查时具有相当大的性能开销。 - Chillersanim
我猜差别不大,但你可以测试一下。 - Patrick Hofman

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...
}

2
牺牲编译时类型安全来避免重复空检查?这也太恶心了吧。 - Ben Voigt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接