能否重载空值合并运算符?

45

在C#中,是否可以重载空值合并运算符来处理类?例如,如果一个实例为null,则返回默认值,如果不是null,则返回该实例。代码可能如下所示:

   return instance ?? new MyClass("Default");  

但如果我想使用空值合并运算符来检查MyClass.MyValue是否已设置该怎么办?


1
Patrik,一个用户:我想重载字符串的null-coalescing运算符,以将空字符串视为null。无论这是否合理,这是另一个问题。 - ANeves
6个回答

31

好问题!在可重载与不可重载运算符列表运算符页面中都没有列出该项。所以我尝试了以下方法:

public class TestClass
{
    public static TestClass operator ??(TestClass  test1, TestClass test2)
    {
        return test1;
    }
}

我得到了错误信息“期望可重载的二元操作符”。因此,我认为答案是,截至 .NET 3.5,并不支持该功能。


只是想提供一个更新:截至2019年9月10日的页面日期,C#的官方文档仍然声明它不能被重载。 - J.D. Mallen

15
根据ECMA-334标准,无法重载??运算符。
同样地,以下运算符也无法被重载:
  • =
  • &&
  • ||
  • ?:
  • ?.
  • checked
  • unchecked
  • new
  • typeof
  • as
  • is

7
简单回答:不行
C#设计原则不允许重载改变语言语义的运算符,因此像复合赋值运算符、三元运算符等复杂运算符是不能被重载的。

3
这被传言为下一个C#版本的一部分。来自http://damieng.com/blog/2013/12/09/probable-c-6-0-features-illustrated

7. 单子空值检查

移除了在访问属性或方法之前检查null的需要。在Groovy中称为Safe Navigation Operator。

之前的写法

if (points != null) {
    var next = points.FirstOrDefault();
    if (next != null && next.X != null) return next.X;
}   
return -1;

之后

var bestValue = points?.FirstOrDefault()?.X ?? -1;

0

我曾试图使用一个我写的结构体来完成这个任务,它与Nullable<T>非常相似。使用Nullable<T>,你可以做如下操作:

Nullable<Guid> id1 = null;
Guid id2 = id1 ?? Guid.NewGuid();

它在将id1Nullable<Guid>隐式转换为Guid时没有问题,尽管Nullable<T>定义了一个显式转换到类型T。但是,对于我的自定义类型,使用相同的方法会出现错误:

运算符'??'不能应用于类型为'MyType'和'Guid'的操作数

因此,我认为编译器内置了一些魔法,为Nullable<T>做了特殊处理。作为替代方案...

简而言之

我们无法重写??运算符,但如果您想让合并运算符评估底层值而不是类(或结构体,在我的情况下),您只需使用一个方法即可,这样需要非常少的额外按键。对于上面的例子,代码如下:

public struct MyType<T>
{
    private bool _hasValue;
    internal T _value;

    public MyType(T value)
    {
        this._value = value;
        this._hasValue = true;
    }

    public T Or(T altValue)
    {
        if (this._hasValue)
            return this._value;
        else
            return altValue;
    }
}

使用方法:

MyType<Guid> id1 = null;
Guid id2 = id1.Or(Guid.Empty);

这个很好用,因为它是一个结构体,id1本身实际上不能为null。对于一个类,只要你要检查的值被公开,扩展方法就可以处理实例是否为空:

public class MyClass
{
    public MyClass(string myValue)
    {
        MyValue = myValue;
    }

    public string MyValue { get; set; }
}

public static class MyClassExtensions
{ 
    public static string Or(this MyClass myClass, string altVal)
    {
        if (myClass != null && myClass.MyValue != null)
            return myClass.MyValue;
        else
            return altVal;
    }
}

使用方法:

MyClass mc1 = new MyClass(null);
string requiredVal = mc1.Or("default"); //Instead of mc1 ?? "default";

-3
如果有人在这里寻找解决方案,最接近的例子就是这样做:
return instance.MyValue != null ? instance : new MyClass("Default");

4
你没有抓住重点,你已经可以使用原帖的代码实现你提出的内容了。请仔细阅读问题,他不是在问如何实现,而是在问是否有可能重载空值合并运算符——很遗憾,这是不可能的。 - ANeves
我明白他在问如何重载 ?? 运算符,而我看到回复说这是不可能的。因此,我只是提供了一个解决方案,它确实能够产生期望的结果(虽然它不使用 ?? 运算符),以便于任何需要找到有效解决方案的人。 - soniiic
1
+1:因为它相关且明确说明这不是真正的答案,而是一个相似的例子。 - Jaap
9
如果instance为null,这将抛出NullReferenceException。你需要将它修改为(instance != null && instance.MyValue != null) ? instance : new MyClass("Default"); - Bobson

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