Operator '?' cannot be applied to operand of type 'T' (2)

I came across a weird behavior of C# compiler (VS 2015). In the code bellow, compiler is happy with Value2, but complains about Value1: Operator '?' cannot be applied to operand of type 'T'

Why?

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;
}
Jon Skeet
people
quotationmark

I believe the problem is that the compiler can't know the type of the expression _valueProvider?.Value.

Let's simplify this a bit:

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

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

What should the compiler infer the type of mystery to be?

  • If T is a reference type or a nullable value type, it would make sense for the expression (and therefore mystery) to be of type T.

  • If T is a non-nullable value type, it would make sense for the expression (and therefore mystery) to be of type T?.

As there are no constraints on T, there's no suitable type to be used, so there's a (slightly unfortunate) error message.

If the property were of type string, int or int?, all of those would be fine, and the expression would be of type string, int? and int? respectively. But there's no equivalent of that for T.

If you constrain T to be a reference type, it's fine, and the expression is of type T:

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

If you constrain T to be a non-nullable value type it's fine, and the expression is of type 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;
}

But without either constraint, there's no valid translation.

people

See more on this question at Stackoverflow