【问题标题】:Receiving error about nullable type parameter even when parameter has notnull constraint即使参数具有非空约束,也会收到有关可空类型参数的错误
【发布时间】:2020-02-02 09:38:16
【问题描述】:

我有一个通用接口IDataAdapter<T>;接口的实现者应该能够从数据源读取具有Guid ID 的 POCO。 IDataAdapter<T> 有一个方法 Read(Guid id),我想返回一个 T?,其中 null 表示在数据源中找不到匹配项。但是,即使在 IDataAdapter<T> 上使用约束 T : notnull,尝试定义此方法也会出现错误 CS8627: A nullable type parameter must be known to be a value type or non-nullable reference type. Consider adding a 'class', 'struct', or type constraint. 为什么我仍然会收到此错误,即使 T 约束为 notnull

代码(应在 C# 8 环境中使用<Nullable>enable</Nullable>):

interface IDataAdapter<T> where T : notnull
{
    T? Read (Guid id); // error CS8627
}

【问题讨论】:

  • 你用的是什么版本?网络核心 3.0 还是网络标准 2.1?
  • @keysl 核心 3.0.
  • @Manvinder Singh 我不认为这是重复的;该问题/答案早于 c#8 的可为空引用类型。

标签: c# nullable c#-8.0 nullable-reference-types


【解决方案1】:

我认为这个问题与this post 中发生的情况非常相似。

请注意,T? where T : classT? where T : struct 在 CLR 中的表示方式非常不同。前者只是 CLR 类型TCLR 中没有单独的类型来区分 TT? C# 中的 T? 只是增加了 C# 编译器的额外编译时间检查。另一方面,后者由CLR类型Nullable&lt;T&gt;表示。

让我们考虑一下你的方法:

T? Read (Guid id);

这应该如何在 CLR 中表示?返回类型是什么?编译器不知道T是引用类型还是值类型,所以编译器无法决定方法签名应该是:

T Read (Guid id);

或:

Nullable<T> Read (Guid id);

【讨论】:

    【解决方案2】:

    如果您不使用notnull 约束,则会引发相同的错误。您需要使用classstruct 约束来指定该类型。你不需要指定notnull,因为结构总是可以为空的,并且启用了可以为空的引用类型,类也是如此。

    只需添加where T:classwhere T:struct

    参考类型

    如果添加class约束,例如:

    #nullable enable
    
    interface IDataAdapter<T>       
        where T:class
    {
        T? Read (Guid id); // error CS8627
        
        void Something(T input);
    }
    
    class StringAdapter:IDataAdapter<string>
    {
        public string Read(Guid id)=>id.ToString();
        
        public void Something(string input){}
    }
    
    

    以下调用将产生警告:

    var adp=new StringAdapter();
    string? x=null;
    adp.Something(x);  //CS8604: Possible null reference argument ....
    

    值类型

    另一方面,如果参数可以为空,则使用 struct 创建 IntAdapter 会导致编译错误:

    interface IDataAdapter<T>       
        where T:struct
    {
        T? Read (Guid id); // error CS8627
        
        void Something(T input);
    }
    
    
    class IntAdapter:IDataAdapter<int>
    {
        public int? Read(Guid id)=>id.ToString().Length;
        
        public void Something(int input){}
    }
    
    void Main()
    {
        
        var adp=new IntAdapter();
        int? x=null;
        adp.Something(x);  //CS1503: Cannot convert from int? to int
    }
    

    这是因为编译生成的方法需要 int? 而不是 int

    说明

    原因是编译器必须在每种情况下生成非常不同的代码。对于一个类,它不需要做任何特别的事情。对于一个结构,它必须生成一个 Nullable

    这在Try out Nullable Reference TypesThe issue with T? 部分中进行了解释:

    可空值类型和可空引用类型之间的区别体现在如下模式中:

    void M&lt;T&gt;(T? t) where T: notnull

    这意味着参数是 T 的可空版本,并且 T 被限制为非空。如果 T 是一个字符串,那么 M 的实际签名将是 M([NullableAttribute] T t),但如果 T 是一个 int,那么 M 将是 M(Nullable t)。这两个签名本质上是不同的,而且这种差异是不可调和的。

    由于可空引用类型和可空值类型的具体表示之间存在这个问题,T 有什么用?还必须要求您将 T 限制为类或结构。

    【讨论】:

      【解决方案3】:

      如果您查看Nullable Struct 的文档,您会发现它必须是struct

      public struct Nullable<T> where T : struct
      

      我相信您需要将 T 限制为 struct

      interface IA<T> where T : struct
      {
          T? Read(Guid id);
          // Or Nullable<T> Read(Guid id);
      }
      
      
      class A : IA<int>
      {
          public int? Read(Guid id) { Console.WriteLine("A"); return 0; }
      }
      

      顺便说一句。你能给我们举个例子,说明你想把这个类与什么类型一起使用吗?

      为什么不直接使用where T: class 并返回T(甚至根本没有约束)?

      interface IA<T> where T : class
      {
          T Read(Guid id);
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-05-24
        • 2020-03-27
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多