运算符“?”不能应用于类型为“T”的操作数(2)
运算符“?”不能应用于类型为“T”的操作数(2)
我在C#编译器(VS 2015)中遇到了一个奇怪的行为。
在下面的代码中,编译器对Value2感到满意,但对Value1抱怨:"运算符'?'不能应用于类型为'T'的操作数"
为什么呢?
public interface IValueProvider{ T Value { get; } } class Validator { public Validator(IValueProvider provider) { _valueProvider = provider; } public T Value1 => _valueProvider?.Value ?? default(T); public T Value2 => _valueProvider != null ? _valueProvider.Value : default(T); private readonly IValueProvider _valueProvider; }
这段代码中出现了一个错误,错误信息为“Operator '?' cannot be applied to operand of type 'T' (2)”。该错误出现在代码的`provider?.Value`这一行。Resharper给出了以下提示:“Can't lift conditional access expression type T to nullable type”。
错误的原因是,`provider.Value`的类型是`T`,而`T`并没有被限定,所以可以传递一个非可空类型给`T`。假设我们这样使用代码`var validator = new Validator
var providerValue = provider?.Value;
即
int providerValue = null;
这是不允许的,因为`int`是不可空类型。
解决方法是,将`T`限定为可空类型,例如使用`where T : class`来进行限定,这样就可以解决该问题。
简单来说,该错误指的是`.Value`,而不是`provider`。你在哪里看到了“Can't lift…”的信息?是在VS2017中使用Resharper时看到的。
问题的原因是编译器无法确定表达式_valueProvider?.Value的类型。在这个例子中,如果T是引用类型或可为空的值类型,那么mystery的类型应该是T。如果T是非可为空的值类型,那么mystery的类型应该是T?。由于T没有任何约束,因此没有合适的类型可用,因此会出现错误信息。
如果属性的类型是string、int或int?,那么这些类型都是合适的,表达式的类型分别是string、int?和int?。但是对于T来说,没有相应的类型。
如果将T约束为引用类型,那么mystery的类型就是T:
public static void Foo(IValueProvider provider) where T : class { // Variable is of type T var mystery = provider?.Value; }
如果将T约束为非可为空的值类型,那么mystery的类型就是T?(即Nullable
public static void Foo(IValueProvider provider) where T : struct { // Variable is of type Nullable var mystery = provider?.Value; }
但是如果没有任何约束,就没有有效的转换方法。为了解决这个问题,可以根据实际需求给T添加适当的约束。