Использование нулевого условного оператора в левой части присваивания

У меня есть несколько страниц, каждая из которых имеет свойство с именем Data. На другой странице я устанавливаю эти данные следующим образом:

if (MyPage1 != null)
    MyPage1.Data = this.data;
if (MyPage2 != null)
    MyPage2.Data = this.data;
if (MyPage3 != null)
    MyPage3.Data = this.data;

Есть ли возможность использовать условный оператор null для MyPage? Я думаю о чем-то вроде этого:

MyPage?.Data = this.data;

Но когда я пишу это так, я получаю следующую ошибку:

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

Я знаю, это потому, что MyPage может быть нулевым, а левая часть больше не будет переменной.

Дело не в том, что я не могу использовать его, как будто он у меня уже есть, но я просто хочу знать, есть ли возможность использовать для этого оператор с нулевым условием.


person diiN__________    schedule 09.03.2016    source источник
comment
Вы должны быть в состоянии создать метод SetData и выполнить MyPage1?.SetData(this.data);   -  person Joachim Isaksson    schedule 09.03.2016
comment
Нулевой оператор распространения/условия предназначен для доступа к свойствам, а не для их установки. Следовательно, вы не можете его использовать.   -  person Tal Avissar    schedule 09.03.2016
comment
Я лично думаю, что это ошибка в текущей реализации. Свойство с левой стороны является сокращением для вызова метода установки свойств, поэтому вы должны иметь возможность использовать ? для нулевого свойства, как если бы вы явно вызвали сам метод set.   -  person Bob Provencher    schedule 19.04.2017


Ответы (7)


Оператор распространения null возвращает значение. И поскольку у вас должна быть переменная в левой части присваивания, а не значение, вы не можете использовать ее таким образом.

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

Комментарий Иоахима Исакссона к вашему вопросу показывает другой подход, который должен работать.

person Allmighty    schedule 09.03.2016

Как предложил Иоахим Исакссон в комментариях, теперь у меня есть метод SetData(Data data) и я использую его следующим образом:

MyPage1?.SetData(this.data);
MyPage2?.SetData(this.data);
MyPage3?.SetData(this.data);
person diiN__________    schedule 09.03.2016

Я придумал следующее расширение,

public static class ObjectExtensions
{
    public static void SetValue<TValue>(this object @object, string propertyName, TValue value)
    {
        var property = @object.GetType().GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance);
        if (property?.CanWrite == true)
            property.SetValue(@object, value, null);
    }
}

Который можно назвать глобально; это работает только с публичными свойствами.

myObject?.SetValue("MyProperty", new SomeObject());

Следующая улучшенная версия работает на чем угодно,

public static void SetValue<TObject>(this TObject @object, Action<TObject> assignment)
{
    assignment(@object);
}

А также может называться глобально,

myObject?.SetValue(i => i.MyProperty = new SomeObject());

Но имя расширения несколько вводит в заблуждение, так как Action не требует присваивания исключительно.

person Community    schedule 23.01.2018

Довольно поздно на вечеринку, но я пришел к этой статье с похожей проблемой. Я взял идею метода SetValue и создал общий метод расширения, как показано ниже:

/// <summary>
/// Similar to save navigation operator, but for assignment. Useful for += and -= event handlers. 
/// If <paramref name="obj"/> is null, then <paramref name="action"/> is not performed and false is returned.
/// If <paramref name="obj"/> is not null, then <paramref name="action"/> is performed and true is returned.
/// </summary>
public static bool SafeAssign<T>(this T obj , Action<T> action ) where T : class 
{
  if (obj is null) return false;
  action.Invoke(obj);
  return true;
}

Пример использования для присоединения и отсоединения обработчика события:

public void Attach() => _control.SafeAssign(c => c.MouseDown += Drag);

public void Detach() => _control.SafeAssign(c => c.MouseDown-= Drag);

Надеюсь, кому-нибудь пригодится :)

person Paul Williams    schedule 05.10.2018

Попробуйте это. Добавьте все свои страницы в myPageList.

IEnumerable<MyPage> myPageList;

foreach(MyPage myPage in myPageList)
{
if (myPage != null)
    myPage.Data = this.data;
}
person Nithila Shanmugananthan    schedule 09.03.2016

Вы можете использовать метод расширения

 public static void NCC<T>(this T instance, System.Action<T> func)
        where T : class
{
        if (instance != null)
        {
            func(instance);
        }
}

MyPage1.NCC(_=>_.Data = this.data);
person liyonghelpme    schedule 29.09.2020

Общий метод расширения SetValue (но только для свойств ref):

    public static void SetValue<T>(this T property, T value)
    {
        property = value;
    }

И будет использоваться как

ButtonOrNull?.Visibility.SetValue(Visibility.Hidden);
person oo_dev    schedule 29.05.2017
comment
Вы проверяли это? Абсолютно никак это не сработает... T property должен быть параметром ref, чтобы изменение закрепилось, потому что единственное, что вы делаете, это устанавливаете локальную копию свойства внутри функции, и у вас не может быть расширения this ref также C# не разрешает доступ по ссылке к свойствам (в отличие от VB, что на самом деле является настоящим позором). - person Mike Marynowski; 29.09.2017
comment
О, вы совершенно правы ???? - работает только с параметрами ref (поэтому в моем случае всегда работало...случайно) - person oo_dev; 05.10.2017
comment
@MikeMarynowski: На самом деле теперь у вас может быть ref this методов расширения (но ограничение, запрещающее передачу свойства параметру ref, все еще существует) - person Ben Voigt; 13.07.2021
comment
@BenVoigt Однако не для неограниченного T. Вы можете иметь только this ref для типов значений. - person Mike Marynowski; 14.07.2021