运算符“?”不能应用于类型为“T”的操作数(2)

13 浏览
0 Comments

运算符“?”不能应用于类型为“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;
}

0
0 Comments

这段代码中出现了一个错误,错误信息为“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(null);`,那么会发生以下情况:

var providerValue = provider?.Value;

int providerValue = null;

这是不允许的,因为`int`是不可空类型。

解决方法是,将`T`限定为可空类型,例如使用`where T : class`来进行限定,这样就可以解决该问题。

简单来说,该错误指的是`.Value`,而不是`provider`。你在哪里看到了“Can't lift…”的信息?是在VS2017中使用Resharper时看到的。

0
0 Comments

问题的原因是编译器无法确定表达式_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添加适当的约束。

0