С# 6.0 Null Propagation Operator & Assignment
Этот вопрос был полностью пересмотрен в интересах тщательного объяснения.
Я заметил, что, по-видимому, довольно плохое ограничение оператора распространения NULL в С# 6.0 тем, что вы не можете вызвать свойство seters для объекта, который был распространен в NULL (хотя вы можете вызвать свойство getters против объекта, который был передан в ноль). Как вы увидите из сгенерированного ИЛ (который я отразил на С#), нет ничего, что должно было бы ограничить возможность вызова средств определения свойств с использованием нулевого распространения.
Для начала я создал простой класс с использованием методов 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(...)
, работает, и вы также можете заметить, что также работает значение свойства, распространяемого в нуле.
Посмотрим на С#, который был сгенерирован из этого кода:
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
и что при использовании против getter Name
свойство тернарного оператора используется либо для получения значения Name
или 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
, так и тернарный оператор, чтобы проверить, является ли человек null
, прежде чем пытаться назначить его свойство Name
. оператор if
работает, как ожидалось; утверждение, использующее тернарный оператор, терпит неудачу, как ожидалось
Ссылка на объект не установлена в экземпляр объекта.
На мой взгляд, ограничение связано с возможностью С# 6.0 преобразовывать нулевое распространение в оператор if
или тройное выражение. Если бы он был разработан для использования только операторов if
, присвоение свойств работало бы с помощью пустого распространения.
До сих пор я не видел одного убедительного аргумента относительно того, почему это НЕ ДОЛЖНО быть возможным, поэтому я все еще ищу ответы!
Ответы
Ответ 1
Ты не единственный! SLaks поднял это как проблему (теперь здесь)
Почему я не могу написать такой код?
Process.GetProcessById(2)?.Exited += delegate { };
и после того, как это было кратко закрыто как "По замыслу"
? Оператор никогда не выдает lvalue, так что это сделано специально.
кто-то сказал, что это было бы хорошо для установщиков свойств, а также обработчиков событий
Может быть, добавить также свойства сеттеров в запрос, как:
Object?.Prop = false;
и он был вновь открыт как запрос функции для С# 7.
Ответ 2
Вы не можете использовать оператор NULL-распространения таким образом.
Этот оператор позволяет распространять нули при оценке выражения. Он не может использоваться как цель назначения точно так, как предполагает ошибка.
Вам нужно придерживаться простой старой проверки:
if (a != null)
{
a.Value = someValue;
}
Ответ 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; }
}
}