是否可以在具有运算符重载的结构上禁止从null进行隐式转换?

时间:2018-05-16 19:03:30

标签: c# operator-overloading nullable

通常,对于value(struct)类型,与null(或对象类型)的比较将导致编译器错误。

struct Value
{
}

class Program
{
    static void Main()
    {
        object o = new object();
        Value v = default;

        // Error CS0019  Operator '==' cannot be applied to operands of type 'Value' and '<null>'
        var a = v == null;

        // Error CS0019  Operator '==' cannot be applied to operands of type 'Value' and 'object'
        var b = v == o;
    }
}

但是,如果我在结构上添加相等运算符重载,则与null的比较不再产生编译器错误:

struct Value
{
    public static bool operator ==(Value l, Value r)
    {
        return true;
    }

    public static bool operator !=(Value l, Value r)
    {
        return true;
    }
}

class Program
{
    static void Main()
    {
        object o = new object();
        Value v = default;

        // compiler is now happy with this.
        var a = v == null;

        // Error CS0019  Operator '==' cannot be applied to operands of type 'Value' and 'object'
        var b = v == o;
    }
}

我认为这与隐式转换为Nullable<Value>有关,但我无法记住细节。问题是:是否可以在保留编译器错误的同时在结构上重载这些运算符?

我已经重构了一些代码,我认为由于这个问题,代码库中有一些地雷。我还担心未来的代码可能会意外地以这种形式编写,我真的很喜欢它产生编译错误(无需实现分析器)。

1 个答案:

答案 0 :(得分:1)

这是因为编译器会自动生成所谓的提升运算符,这些运算符也可以使用可空值类型,因为您可以在docs中读取。因此,对于给定的比较运算符T, U -> bool,其中TU是不可为空的值类型,还存在提升的运算符T?, U -> boolT, U? -> boolT?, U? -> bool

要取消这些运算符,您可以明确定义它们并使用Obsolete属性进行装饰,error参数设置为true,如下所示:

struct Value
{
    public static bool operator ==(Value l, Value r)
    {
        return true;
    }

    public static bool operator !=(Value l, Value r)
    {
        return true;
    }

    [Obsolete("Some error message", error: true)]
    public static bool operator ==(Value? l, Value r) => 
        throw new NotImplementedException();

    [Obsolete("Some error message", error: true)]
    public static bool operator ==(Value l, Value? r) => 
        throw new NotImplementedException();

    [Obsolete("Some error message", error: true)]
    public static bool operator ==(Value? l, Value? r) => 
        throw new NotImplementedException();

    [Obsolete("Some error message", error: true)]
    public static bool operator !=(Value? l, Value r) => 
        throw new NotImplementedException();

    [Obsolete("Some error message", error: true)]
    public static bool operator !=(Value l, Value? r) => 
        throw new NotImplementedException();

    [Obsolete("Some error message", error: true)]
    public static bool operator !=(Value? l, Value? r) => 
        throw new NotImplementedException();
}

现在在new Value() == nullnew Value() == (Value?)null等比较中,将选择上述匹配的用户定义运算符,因为它更具体,并且会给出这样的错误:

  

错误CS0619:&#39; Value.operator ==(价值,价值?)&#39;已过时:&#39;一些错误消息&#39;