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;
}
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.
See more on this question at Stackoverflow