代码之家  ›  专栏  ›  技术社区  ›  Charles

方法参数后的双感叹号[重复]

c#
  •  0
  • Charles  · 技术社区  · 4 年前

    从…起 https://source.dot.net/#System.Private.CoreLib/Hashtable.cs,475 :

    public virtual bool ContainsKey(object key!!)
    

    它看起来像两个允许null的运算符。有关于它的文件吗?

    0 回复  |  直到 4 年前
        1
  •  113
  •   canton7    4 年前

    这是一个空参数检查语法,它将在C#11中引入。该提案 since been rolled back 关注社区反馈。

    提案 is here ,而PR正在向运行时进行首次推出 is here 。

    语法:

    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);
            }
        }
    }
    

    See on SharpLab 。

    方法包含 throw 语句不太可能被JIT内联,因此使用throw-helper使您的方法更有可能被内联,这可能会完全删除null检查! See on SharpLab 。

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


    有几个地方 !! 将使编译器生成您无法(轻松)手动编写的代码。

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

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

    为了正确地将 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);
    }
    

    另一个地方 !! 在链式构造函数调用中执行一些您自己无法编写的操作:

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

    此null检查 s 在访问之前 s.Length ,类似于以下内容(这不是有效的C#):

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

    See on SharpLab 。

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

    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();
        }
    }
    

    也就是说,null检查发生在 ExpensiveObject 。 See on SharpLab 。

        2
  •  7
  •   Okan Karadag    4 年前

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

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

    我们尽早预览了参数null检查,因为 预期反馈。此功能允许!!在参数的末尾 name,以便在方法开始前提供参数null检查 处决我们在C#11早期就包含了此功能,以最大限度地提高 我们从GitHub评论、MVP、社交媒体中收集的反馈, 会议听众、与用户的个人对话以及# 设计团队正在进行反思。我们收到了广泛的反馈 关于这一功能,我们对此表示赞赏。

    我们从中获得的反馈和广泛的见解 反馈使我们重新考虑将其作为C#11特性。我们没有 充分相信这是C#的正确功能设计,并且 正在将其从C#11中删除。我们可能会在 稍后日期。

        3
  •  2
  •   Stas Sorokin    4 年前

    新的C#11参数零检查功能 !!

    双感叹号 !! 是 参数空检查功能 即替换以下(旧的)null检查:

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

    使用这种新的空检查方式,相同的代码要短得多:

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

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

    详尽阐述

    基本上,使用此代码可以确保 object arg 不是 无效的

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

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

    简化代码示例:

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

    根据最新的PR说明,此方法可用于 !! 不可能:

    ArgumentNullException.ThrowIfNull用于 !! 不可能,但方法调用

    以下PR页面更新说明 !! 目前只能在方法参数中使用,尽管C#语言团队正在考虑将此运算符添加到属性、局部变量和任意表达式中:

    注意!!目前仅限于应用于方法自变量, 因为这是绝大多数用例(如更改所示 在本PR中制作)

    扩展代码示例,其中 !! 不可用(对于我们要检查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
    

    测试此代码 here