即使参数具有非空约束,也会收到有关可空类型参数的错误
Posted
技术标签:
【中文标题】即使参数具有非空约束,也会收到有关可空类型参数的错误【英文标题】: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
【问题讨论】:
Generic Constraint for Non Nullable types的可能重复 你用的是什么版本?网络核心 3.0 还是网络标准 2.1? @keysl 核心 3.0. @Manvinder Singh 我不认为这是重复的;该问题/答案早于 c#8 的可为空引用类型。 【参考方案1】:我认为这个问题与this post 中发生的情况非常相似。
请注意,T? where T : class
和 T? where T : struct
在 CLR 中的表示方式非常不同。前者只是 CLR 类型T
。 CLR 中没有单独的类型来区分 T
和 T?
。 C# 中的 T?
只是增加了 C# 编译器的额外编译时间检查。另一方面,后者由CLR类型Nullable<T>
表示。
让我们考虑一下你的方法:
T? Read (Guid id);
这应该如何在 CLR 中表示?返回类型是什么?编译器不知道T
是引用类型还是值类型,所以编译器无法决定方法签名应该是:
T Read (Guid id);
或:
Nullable<T> Read (Guid id);
【讨论】:
【参考方案2】:如果您不使用notnull
约束,则会引发相同的错误。您需要使用class
或struct
约束来指定该类型。你不需要指定notnull
,因为结构总是可以为空的,并且启用了可以为空的引用类型,类也是如此。
只需添加where T:class
或where 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 Types 的The issue with T?
部分中进行了解释:
可空值类型和可空引用类型之间的区别体现在如下模式中:
void M<T>(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);
【讨论】:
以上是关于即使参数具有非空约束,也会收到有关可空类型参数的错误的主要内容,如果未能解决你的问题,请参考以下文章