C#中的双感叹号是什么?

shi*_*ngo 82 c# syntax function method-signature c#-11.0

来自https://source.dot.net/#System.Private.CoreLib/Hashtable.cs,475

public virtual bool ContainsKey(object key!!)
Run Code Online (Sandbox Code Playgroud)

它看起来像两个 null-forgiving 运算符。有相关文件吗?

can*_*on7 113

这是一个空参数检查语法,将在 C# 11 中引入。根据社区反馈,该提案已被回滚

该提案在这里,首次向运行时推出的 PR在这里

语法:

public void Foo(string bar!!)
{
}
Run Code Online (Sandbox Code Playgroud)

大致相当于:

public void Foo(string bar)
{
    if (bar is null)
    {
        throw new ArgumentNullException(nameof(bar));
    }
}
Run Code Online (Sandbox Code Playgroud)

...虽然实际的实现使用了一个 throw helper,比如:

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

请参阅 SharpLab

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

请注意,使用!!是方法的实现细节:它只会导致编译器插入您本来可以自己编写的代码。这意味着从throw!!(或反之亦然)并不是重大变化。


然而,有几个地方!!会让编译器生成您无法(轻松)手工编写的代码。

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

public record Person(string Name!!, int Age);
Run Code Online (Sandbox Code Playgroud)

为了正确地对Name以前版本的 C# 中的参数进行空检查,您必须手写如下:

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);
}
Run Code Online (Sandbox Code Playgroud)

!!另一个你无法自己编写的东西的地方是在链式构造函数调用中:

public class C
{
    public C(int i) { }
    public C(string s!!) : this(s.Length) { }
}
Run Code Online (Sandbox Code Playgroud)

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

public C(string s)
{
    if (s is null)
        throw new ArgumentNullException(nameof(s));
    C(s.Length);
}
Run Code Online (Sandbox Code Playgroud)

请参阅 SharpLab

另一个有趣的方面是,空检查是在构造函数中的字段赋值之前插入的。例如:

public class C
{
    private readonly ExpensiveObject e = new ExpensiveObject();
    public C(string s!!) { }
}
Run Code Online (Sandbox Code Playgroud)

编译为:

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

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


Oka*_*dag 7

According to the latest updates published by microsoft about c# 11, this feature seems to have been removed.

\n

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

\n
\n

We previewed parameter null-checking as early as possible because we\nanticipated feedback. This feature allows !! on the end of a parameter\nname to provide parameter null checking before the method begins\nexecution. We included this feature early in C# 11 to maximize\nfeedback, which we gathered from GitHub comments, MVPs, social media,\na conference audience, individual conversations with users, and the C#\ndesign team\xe2\x80\x99s ongoing reflection. We received a wide range of feedback\non this feature, and we appreciate all of it.

\n

The feedback and the wide range of insight we gained from this\nfeedback led us to reconsider this as a C# 11 feature. We do not have\nsufficient confidence that this is the right feature design for C# and\nare removing it from C# 11. We may return to this area again at a\nlater date.

\n
\n