【问题标题】:Operator '?' cannot be applied to operand of type 'T' (2)操作员 '?'不能应用于“T”类型的操作数 (2)
【发布时间】:2017-05-25 07:26:13
【问题描述】:

我遇到了 C# 编译器 (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;
}

【问题讨论】:

  • 这并不奇怪。 ? 对于不可为空的类型(即结构)没有任何意义。如果没有类型约束,编译器不知道 T 是否是结构。 Value2 不会在任何地方使用此运算符,这就是它起作用的原因。
  • @PanagiotisKanavos:但是_valueProvider 不是T 类型,而是IValueProvider&lt;T&gt; 类型。
  • @CodeCaster 相反,非常清楚,sn-p 重现了问题。只需在 LinqPad 中尝试一下,您就会立即解决问题。
  • @CodeCaster 这段代码足以让其他人重现和理解错误。答案应该清楚地表明编译器错误与 Value 有关,因为每个人都认为错误与提供程序有关
  • @CodeCaster:说实话,比实际需要的多一点,但不是很多。这是一个绝对最小的例子吗?不。它是否足够接近以避免大量完全不相关的代码?绝对地。它是否完整,可以很容易地重现问题?是的。

标签: c#


【解决方案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 是引用类型或可为空的值类型,则表达式(因此mystery)的类型为T 是有意义的。

  • 如果T不可为空 值类型,则表达式(因此mystery)的类型为T? 是有意义的。

由于T 没有任何限制,因此没有合适的类型可以使用,因此出现了一条(有点遗憾)错误消息。

如果属性的类型为stringintint?,所有这些都可以,表达式将分别为stringint?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?(又名Nullable&lt;T&gt;)。

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&lt;int&gt;(null); 一起使用,那么就会发生这种情况:

    var providerValue = provider?.Value;
    

    int providerValue = null;
    

    这是不允许的,因为 int 不能为空。

    其余的错误会从那里慢慢下降。所以将T 约束为可以为空的类型,例如where T : class,它会起作用。

    【讨论】:

    • 简单来说,错误指的是.Value,而不是provider
    • 您在哪里看到“无法解除...”消息?
    • @Henk VS2017 与 ReSharper。
    猜你喜欢
    • 2015-12-11
    • 2013-06-09
    • 2013-11-06
    • 2016-05-15
    • 1970-01-01
    • 1970-01-01
    • 2016-04-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多