C # 中的双叹号是什么?

来自 < a href = “ https://source.dot.net/# System.Private.CoreLib/网址1,475”rel = “ noReferrer”> https://source.dot.net/#system:

public virtual bool ContainsKey(object key!!)

它看起来像两个空宽容运算符。有关于它的文档吗?

7004 次浏览

这是一个空参数检查语法,将在 C # 11中引入。这个建议有 已经有一段时间了跟随社区的反馈。

提案 在这里和 PR 首次向运行时 在这里推出。

语法:

public void Foo(string bar!!)
{
}

大致相当于:

public void Foo(string bar)
{
if (bar is null)
{
throw new ArgumentNullException(nameof(bar));
}
}

... 尽管实际的实现使用了一个 throw 助手,比如:

public void Foo(string bar)
{
<PrivateImplementationDetails>.ThrowIfNull(bar, "bar");
}


[CompilerGenerated]
internal sealed class <PrivateImplementationDetails>
{
internal static void Throw(string paramName)
{
throw new ArgumentNullException(paramName);
}


internal static void ThrowIfNull(object argument, string paramName)
{
if (argument == null)
{
Throw(paramName);
}
}
}

参见 SharpLab。

包含 throw语句的方法不太可能被 JIT 内联,因此使用抛出助手使您的方法更有可能被内联,这可能会完全删除空检查!参见 SharpLab.

请注意,!!的使用是方法的一个实现细节: 它只是导致编译器插入代码,而这些代码本来可以由您自己编写。这意味着从 throw移动到 !!(反之亦然)并不是一个突破性的改变。


有几个地方,!!将得到编译器生成的代码,你不能(容易地)手写,但是。

!!特别有用的一个地方是主构造函数的记录。例如:

public record Person(string Name!!, int Age);

为了在以前的 C # 版本中正确地使用 null 检查 Name参数,您必须写出以下内容:

public record Person
{
public string Name { get; init; }
public int Age { get; init; }
    

public Person(string name, int age)
{
if (name is null)
throw new ArgumentNullException(nameof(name));
(Name, Age) = (name, age);
}
    

public void Deconstruct(out string name, out int age) =>
(name, age) = (Name, Age);
}

!!做一些你自己不能写的事情的另一个地方是在链式构造函数调用中:

public class C
{
public C(int i) { }
public C(string s!!) : this(s.Length) { }
}

这个空值在访问 s.Length之前检查 s,如下所示(不是有效的 C #) :

public C(string s)
{
if (s is null)
throw new ArgumentNullException(nameof(s));
C(s.Length);
}

参见 SharpLab。

另一个有趣的方面是,空检查是在构造函数中的字段分配之前插入的:

public class C
{
private readonly ExpensiveObject e = new ExpensiveObject();
public C(string s!!) { }
}

汇编如下:

public class C
{
private readonly ExpensiveObject e;
public C(string s)
{
if (s is null)
throw new ArgumentNullException(nameof(s));
e = new ExpensiveObject();
}
}

也就是说,空检查发生在 ExpensiveObject.参见 SharpLab的实例化之前。

新的 C # 11参数空值检查特性 !!

双叹号的 !!是一个取代以下(旧的) null 检查的 参数无效检查特性:

void Bar(object arg)
{
if (arg is null)
{
throw new ArgumentNullException(nameof(arg));
}
Console.WriteLine("Hi");
// use arg...
}

使用这种新的 null 检查方法,相同的代码会更短:

void Bar(object arg!!)
{
Console.WriteLine("Hi");
// use arg...
}

如果参数是 null,这两个方法(以及下一个方法)都会抛出 ArgumentNullException

详细说明

基本上,使用这段代码可以确保 object arg不是 null

另一种方式: ArgumentNullException.ThrowIfNull(parameter)

编辑: 该静态方法是在 C # 10(. NET 6)中引入的,并且依赖于.NET 6版本

简化代码示例:

void Greeting(string name)
{
ArgumentNullException.ThrowIfNull(name);
Console.WriteLine($"Hi {name}");
}

有了最新的公关指示,这种方法可以在 !!不可能的地方使用:

ThrowIfNull 用于不能使用 !!,但方法调用可以使用

下面的 PR 页面更新指出,目前 !!只能用于方法参数,尽管 C # 语言团队正在考虑将这个操作符添加到属性、局部变量和任意表达式中:

注意! ! 目前仅限于应用于方法参数, 因为这是绝大多数用例(这些变化就是例证) )

!!不可用的情况下展开的代码示例(对于要检查是否为 null 的变量) :

void GetUserCities(string userId!!)
{
// suppose you got this data (that is inconsistent and may contain a null)
// from an API request by the userId
var cities = new Dictionary<string, string>(){
{"UK", "London"},
{"USA", "New York"},
{"India", "New Delhi"},
{"Wakanda", null},
};
foreach(var pair in cities) {
try {
ArgumentNullException.ThrowIfNull(pair.Value);
Console.WriteLine("Country: " + pair.Key + ", City:" + pair.Value);
} catch(System.ArgumentNullException) {
Console.WriteLine("Could not find a city for this country: " + pair.Key);
}
}
}

产出:

Country: UK, City:London
Country: USA, City:New York
Country: India, City:New Delhi
Could not find a city for this country: Wakanda

测试代码 这里

根据微软发布的关于 c # 11的最新更新,这个功能似乎已经被删除了。

资料来源: https://devblogs.microsoft.com/dotnet/csharp-11-preview-updates/#remove-parameter-null-checking-from-c-11

我们尽早预览了参数 null 检查,因为我们 预期的反馈。这个特性允许! ! 在参数的末尾 在方法开始之前提供参数 null 检查 我们在 C # 11的早期包含了这个特性来最大化 我们从 GitHub 的评论 MVP 社交媒体上收集到的反馈, 会议听众、与用户的个人对话以及 C # 我们收到了广泛的反馈 我们非常感激。

我们从中得到的反馈和广泛的见解 反馈让我们重新考虑这个 C # 11特性。我们没有 足够的信心,这是正确的特性设计的 C # 和 正在从 C # 11中移除它。我们可能会再次回到这个区域 以后再说。