我有这个代码片段
public static void Main()
{
var t = new Test<uint>();
t.Run(null);
}
public class Test<T> where T: notnull
{
public void Run(T? value)
{
Console.WriteLine(value is null);
}
}
我希望能够说 T 不可为空,但对于我的方法,我想接受可为空类型。然而这不起作用,因为 Run 的签名是
uint
而不是 uint?
。
引用类型的情况并非如此,它们按照我期望的方式工作,能够具有可为空的签名。
这里发生的事情不允许值类型具有可为空的方法签名。我发现如果我从 notnull 更改为 'struct' 它工作正常,但我希望能够接受引用和值类型。
这里发生了什么不允许值类型具有可为空的方法签名。
基本上没有办法在 IL 中表示这一点。
在 T?
与
Test<int>
的情况下,Test<string>
的含义存在根本性的差异。在前一种情况下,
T?
表示完全不同的类型,Nullable<int>
,而在后者中,T?
表示“就 CLR 而言,string
,但在编译时 null 分析期间允许 null”。
使用
T?
和 notnull
约束肯定会遇到一些奇怪的情况 - 约束很有用,但它绝对不是您想要或期望的。例如,您可以声明类型为 T?
的 field,但最终它在 IL 中只是
T
:
public class Test<T> where T : notnull
{
private T? field;
public Test(T value) => field = value;
public Test() {}
public bool FieldIsNull => field == null;
}
class Program
{
static void Main()
{
Test<string> test1 = new Test<string>();
Console.WriteLine(test1.FieldIsNull); // True
Test<int> test2 = new Test<int>();
Console.WriteLine(test2.FieldIsNull); // False
}
}
如果有什么安慰的话,ECMA C# 标准化小组正在努力“干净地”指定这一点 - 这非常棘手......