C# 6.0 Оператор Распространения Null И Присвоение Свойств


этот вопрос был полностью переработан в интересах тщательного объяснения.

Я заметил, что кажется довольно слабым ограничением оператора распространения null в C# 6.0 в том, что вы не можете вызвать свойства setters против объекта, который был распространен null (хотя вы можете вызвать свойства getters против объекта, который был распространен null). Как вы увидите из сгенерированного IL (который у меня есть отражено в C#) , нет ничего, что должно ограничивать возможность вызова установщиков свойств с использованием распространения null.

Для начала я создал простой класс, содержащий методы Get / Set в стиле Java и свойство с открытым доступом 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;
    }
}

Левая сторона присваивания должна быть переменной, свойством или индексатор.

Вы можете обратите внимание, однако, что Java-способ установки имени, вызывая SetName(...) работает, и вы также можете заметить, что получение значения 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 распространение null преобразуется в простой оператор if, а при использовании метода Name для получения значения Name используется тернарный оператор. null. Одна вещь, которую я заметил здесь, - это различие в поведении между использованием оператора if и использованием тернарного оператора: при использовании сеттера использование оператора 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, так и тернарный оператор, чтобы проверить, является ли человек null, прежде чем пытаться присвоить его Name свойству. оператор if работает как ожидалось; оператор, использующий тернарный оператор выходит из строя, как и ожидалось

Ссылка на объект не установлена на экземпляр объекта.

На мой взгляд, ограничение исходит из способности C# 6.0 преобразовывать распространение null либо в оператор if, либо в тернарное выражение. Если бы он был разработан для использования только операторов if, присвоение свойств работало бы через распространение null. До сих пор я не видел ни одного убедительного аргумента, почему это не должно быть возможно, поэтому я все еще ищу ответы!
3 24

3 ответа:

Ты не единственный! Слакс поднял этот вопрос как Вопрос

Почему я не могу написать такой код?

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

И после того, как он был ненадолго закрыт как "по замыслу"

Что ?. Оператор не производит значения, так это на дизайн.

Кто-то заметил, что это было бы хорошо для установщиков свойств, а также обработчиков событий

Может быть, добавить также свойства setters в запрос, как:

Object?.Prop = false;

И он был повторно открыт как запрос функции для C#7.

Вы не можете использовать оператор null-propagation таким образом.

Этот оператор позволяет распространять нули при вычислении выражения. Он не может быть использован в качестве цели назначения именно так, как предполагает ошибка.

Вам нужно придерживаться простой старой проверки null:

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

Попробуйте вот так...

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

}