Оператор '?' не может применяться к операнду типа "T" (2)

Я столкнулся с странным поведением компилятора С# (VS 2015). В приведенном ниже коде компилятор удовлетворен Value2, но жалуется на Value1: Operator '?' не может применяться к операнду типа "T"

Почему?

public interface IValueProvider<T>
{
    T Value { get; }
}

class Validator<T>
{
    public Validator(IValueProvider<T> provider)
    {
        _valueProvider = provider;
    }

    public T Value1 => _valueProvider?.Value ?? default(T);

    public T Value2 => _valueProvider != null ? _valueProvider.Value : default(T);

    private readonly IValueProvider<T> _valueProvider;
}

Ответ 1

Я считаю, что проблема заключается в том, что компилятор не может знать тип выражения _valueProvider?.Value.

Немного упростите это:

public interface IValueProvider<T>
{
    T Value { get; }
}

public class Test
{
    public static void Foo<T>(IValueProvider<T> provider)
    {
        var mystery = provider?.Value;
    }
}

Что должен компилятор выводить тип mystery?

  • Если T является ссылочным типом или типом значения NULL, для выражения (и, следовательно, mystery) было бы иметь тип T.

  • Если T - тип значений, не допускающих нулевой, для выражения (и, следовательно, mystery) было бы иметь тип T?.

Поскольку нет ограничений на T, нет подходящего типа для использования, поэтому есть сообщение (немного неудачное).

Если свойство было типа string, int или int?, все они были бы точными, и выражение было бы типа string, int? и int? соответственно. Но нет эквивалента этого для T.

Если вы ограничиваете T ссылочным типом, это нормально, а выражение имеет тип T:

public static void Foo<T>(IValueProvider<T> provider) where T : class
{
    // Variable is of type T
    var mystery = provider?.Value;
}

Если вы ограничиваете T недействительным типом значения, это отлично, а выражение имеет тип T? (aka Nullable<T>).

public static void Foo<T>(IValueProvider<T> provider) where T : struct
{
    // Variable is of type Nullable<T>
    var mystery = provider?.Value;
}

Но без какого-либо ограничения нет действительного перевода.

Ответ 2

Этот код:

public interface IValueProvider<T>
{
    T Value { get; }
}

public class Validator<T>
{
    public Validator(IValueProvider<T> provider)
    {
        var providerValue = provider?.Value;
    }
}

Показывает эту ошибку при provider?.Value:

Оператор '?' не может применяться к операнду типа "T"

И ReSharper дает этот намек:

Невозможно поднять тип выражения условного доступа T до типа с нулевым значением

Поскольку тип provider.Value равен T, который не является ограниченным, поэтому вы можете передать тип с непустым значением для T.

Предположим, что вы используете его с var validator = new Validator<int>(null);, тогда это произойдет:

var providerValue = provider?.Value;

то есть.

int providerValue = null;

Это недопустимо, поскольку int не может быть нулевым.

Остальные ошибки истощаются оттуда. Поэтому ограничивайте T типом с нулевым значением, например, where T : class, и он будет работать.