【问题标题】:C# 6.0 Null Propagation Operator & Property AssignmentC# 6.0 空传播运算符和属性赋值
【发布时间】:2015-12-07 18:18:48
【问题描述】:

为了解释的彻底,这个问题已经过彻底检查。

我注意到 C# 6.0 中空传播运算符的一个似乎很差的限制,因为您不能针对已空传播的对象调用属性 setters(尽管您可以调用属性getters 针对已传播为 null 的对象)。正如您将从生成的 IL (我已将其反映到 C#)中看到的那样,没有什么可以限制使用 null 传播调用属性设置器的能力。

首先,我创建了一个简单的类,它具有 Java 风格的 Get/Set 方法和一个具有公共 getter/setter 访问权限的属性。

public class Person
{
    public Person(string name, DateTime birthday)
    {
        Name = name;
    }

    public string Name { get; set; }

    public void SetName(string name)
    {
        Name = name;
    }

    public string GetName()
    {
        return Name;
    }
}

我在下面的测试类中测试了空值传播的能力。

public class Program
{
    public static void Main(string[] args)
    {
        Person person = new Person("Joe Bloggs", DateTime.Parse("01/01/1991"));

        // This line doesn't work - see documented error below
        person?.Name = "John Smith";

        person?.SetName("John Smith");

        string name = person?.Name;
    }
}

赋值的左边必须是变量、属性或 索引器。

但您可能会注意到,通过调用 SetName(...) 来设置名称的 Java 方式有效,并且您可能还会注意到获取 null 传播属性的值也有效。

让我们看一下由此代码生成的 C#:

public static void Main(string[] args)
{
    Person person = new Person("Joe Bloggs", DateTime.Parse("01/01/1991"));
    if (person != null)
    {
        person.SetName("John Smith");
    }
    string arg_33_0 = (person != null) ? person.Name : null;
}

请注意,当用于SetName 方法时,空传播转换为简单的if 语句,而当用于Name 属性getter 时,使用三元运算符来获取@987654328 的值@ 或null

我在这里注意到的一件事是使用if 语句和使用三元运算符之间的行为差​​异:使用setter 时,使用if 语句可以工作,而使用三元运算符则不行。

public static void Main(string[] args)
{
    Person person = null;

    if (person != null)
    {
        person.Name = "John Smith";
    }

    person.Name = (person != null) ? "John Smith" : null;
}

在此示例中,我同时使用 if 语句和三元运算符来检查 person 是否为 null,然后再尝试分配其 Name 属性。 if 语句按预期工作;使用三元运算符的语句按预期失败

对象引用未设置为对象的实例。

在我看来,限制来自于 C# 6.0 将 null 传播转换为 if 语句或三元表达式的能力。如果它被设计为仅使用 if 语句,则属性分配将通过 null 传播工作。

到目前为止,我还没有看到一个令人信服的论据来说明为什么这不应该发生,因此我仍在寻找答案!

【问题讨论】:

  • 这不是它的工作原理。运算符在检查 null 后返回一个值。
  • 操作符没什么问题——事实上很多语言都有类似的操作符。它根本不做你认为它做的事,它的名字也没有暗示这一点。事实上,如果它确实以这种方式行事,那将是一个问题 - 运算符的行为会有所不同,具体取决于它是在赋值的左侧还是右侧。
  • 您为什么认为null propagation 与分配有关?它应该比其他语言中的类似名称更明确(例如,为 Java 提出的 null 安全、Groovy 中的安全导航、Kotlin 中的安全调用)它不分配任何东西,而是返回 (如果应用于空对象,则传播)空值
  • 附言。你发布的内容没有做你认为它做的事。它检查someValue 是否为空,它检查aa.SetValuea?.SetValue 在参数方面完全没有区别。
  • 作为解决方法,您可以实现一个通用扩展方法来设置任意类型的属性: public static void SetValue(this T property, T value) { property = value;并像艺术一样使用它?.Prop1.SetValue("Hello");

标签: c# c#-6.0 null-propagation-operator


【解决方案1】:

你不是唯一一个! SLaks 将此提升为 an issue(现在为 here

为什么我不能写这样的代码?

Process.GetProcessById(2)?.Exited += delegate { };

在它被“按设计”短暂关闭后

?.运算符从不产生左值,所以这是设计使然。

有人评论说它对属性设置器和事件处理程序都有好处

也许还会将属性设置器添加到请求中,例如:

Object?.Prop = false;

它作为 C#7 的功能请求重新打开。

【讨论】:

    【解决方案2】:

    您不能以这种方式使用空传播运算符。

    此运算符允许在评估表达式时传播空值。不能完全按照错误提示将其用作分配的目标。

    你需要坚持简单的旧空检查:

    if (a != null)
    {
        a.Value = someValue;
    }
    

    【讨论】:

    • @series0ne 编辑不检查参数someValue 是否为空,它检查对象a。操作员根本不会做你认为它会做的事情。
    • 我已经更新了整个问题来说明我的问题。它不了解操作员的工作方式——我知道这一点;它可以理解为什么,如果 C# 6.0 可以将 null 传播转换为不同的形式(一种是您建议的答案),为什么它不能为属性分配执行此操作,而它绝对可以这样做 - 只是没有令人信服的论据来说明为什么它不这样做。
    【解决方案3】:

    这样试试吧……

    using System;
    
    namespace TestCon
    {
        class Program
        {
            public static void Main()
        {
    
            Person person = null;
            //Person person = new Person() { Name = "Jack" };
    
            //Using an "if" null check.
            if (person != null)
            {
                Console.WriteLine(person.Name);
                person.Name = "Jane";
                Console.WriteLine(person.Name);
            }
    
            //using a ternary null check.
            string arg = (person != null) ? person.Name = "John" : arg = null;
            //Remember the first statment after the "?" is what happens when true. False after the ":". (Just saying "john" is not enough)
            //Console.WriteLine(person.Name);
    
            if (arg == null)
            {
                Console.WriteLine("arg is null");
            }
    
            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }
    }
    public class Person
    {
        public string Name { get; set; }
    }
    

    }

    【讨论】:

    • person.Name = (person != null) ? “约翰·史密斯”:空; // 在这个语句中如果条件为假,那么“person = null”因此“person.Name”不存在。
    • 个人 person = new Person() { Name = null }; //对象实例必须存在。人名=(人名!=空)? “约翰”:空; Console.WriteLine(person.Name);
    • 这恐怕不能回答问题;您只是在说明绕过 null 传播的方法,每个 C# 开发人员在引入 null 传播运算符之前都会使用这些方法
    猜你喜欢
    • 2021-08-10
    • 2015-02-26
    • 1970-01-01
    • 1970-01-01
    • 2011-07-29
    • 1970-01-01
    • 2021-06-07
    • 2011-11-16
    • 2012-05-14
    相关资源
    最近更新 更多