即使参数具有 notnull 约束,也会收到有关可空类型参数的错误
Receiving error about nullable type parameter even when parameter has notnull constraint
我有一个通用接口IDataAdapter<T>
;接口的实现者应该能够从数据源中读取具有 Guid
ID 的 POCO。 IDataAdapter<T>
有一个方法 Read(Guid id)
我想 return 一个 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
}
如果您查看 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; }
}
顺便说一句。你能给我们举个例子,说明你想把这个 class 用于什么类型吗?
为什么不直接使用 where T: class
和 return T
(甚至根本没有约束)?
interface IA<T> where T : class
{
T Read(Guid id);
}
我认为这个问题与 中发生的事情非常相似。
请注意,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 中表示?什么是 return 类型?编译器不知道T
是引用类型还是值类型,所以编译器无法决定方法签名应该是:
T Read (Guid id);
或:
Nullable<T> Read (Guid id);
如果您不使用 notnull
约束,则会引发相同的错误。您需要使用 class
或 struct
约束指定该类型。您不需要指定 notnull
,因为结构始终可以为 null,并且启用了可为 null 的引用类型,因此 classes.
只需添加 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
。
说明
原因是编译器必须在每种情况下生成非常不同的代码。对于 class,它不需要做任何特别的事情。对于结构,它必须生成一个 Nullable< T>。
这在 Try out Nullable Reference Types 的 The issue with T?
部分有解释:
This distinction between nullable value types and nullable reference types comes up in a pattern such as this:
void M<T>(T? t) where T: notnull
This would mean that the parameter is the nullable version of T, and T is constrained to be notnull. If T were a string, then the actual signature of M would be M([NullableAttribute] T t), but if T were an int, then M would be M(Nullable t). These two signatures are fundamentally different, and this difference is not reconcilable.
Because of this issue between the concrete representations of nullable reference types and nullable value types, any use of T? must also require you to constrain the T to be either class or struct.
我有一个通用接口IDataAdapter<T>
;接口的实现者应该能够从数据源中读取具有 Guid
ID 的 POCO。 IDataAdapter<T>
有一个方法 Read(Guid id)
我想 return 一个 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
}
如果您查看 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; }
}
顺便说一句。你能给我们举个例子,说明你想把这个 class 用于什么类型吗?
为什么不直接使用 where T: class
和 return T
(甚至根本没有约束)?
interface IA<T> where T : class
{
T Read(Guid id);
}
我认为这个问题与
请注意,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 中表示?什么是 return 类型?编译器不知道T
是引用类型还是值类型,所以编译器无法决定方法签名应该是:
T Read (Guid id);
或:
Nullable<T> Read (Guid id);
如果您不使用 notnull
约束,则会引发相同的错误。您需要使用 class
或 struct
约束指定该类型。您不需要指定 notnull
,因为结构始终可以为 null,并且启用了可为 null 的引用类型,因此 classes.
只需添加 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
。
说明
原因是编译器必须在每种情况下生成非常不同的代码。对于 class,它不需要做任何特别的事情。对于结构,它必须生成一个 Nullable< T>。
这在 Try out Nullable Reference Types 的 The issue with T?
部分有解释:
This distinction between nullable value types and nullable reference types comes up in a pattern such as this:
void M<T>(T? t) where T: notnull
This would mean that the parameter is the nullable version of T, and T is constrained to be notnull. If T were a string, then the actual signature of M would be M([NullableAttribute] T t), but if T were an int, then M would be M(Nullable t). These two signatures are fundamentally different, and this difference is not reconcilable.
Because of this issue between the concrete representations of nullable reference types and nullable value types, any use of T? must also require you to constrain the T to be either class or struct.