Можно ли подавить неявное преобразование из null в структуру с перегрузками операторов? - PullRequest
0 голосов
/ 16 мая 2018

Обычно при значениях (структурах) сравнение со значениями 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;
    }
}

Однако, если я добавлю перегруженные операторы равенства в структуру, сравнение с нулем больше не приведет к ошибке компилятора:

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 голосов
/ 22 мая 2018

Это потому, что компилятор автоматически генерирует так называемые поднятые операторы, которые также работают с обнуляемыми типами значений, которые вы можете прочитать в документах .Таким образом, для данного оператора сравнения T, U -> bool, где T и U являются необнуляемыми типами значений, существуют также поднятые операторы T?, U -> bool, T, U? -> bool и T?, 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() == null, а также new Value() == (Value?)null будет выбран вышеуказанный соответствующий пользовательский оператор, так как он более конкретен и будет выдана такая ошибка:

ошибка CS0619: 'Value.operator == (Value, Value?) 'устарел: «Некоторое сообщение об ошибке»

...