45

I have a few pages, each with a property named Data. On another page I'm setting this data like this:

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

Is there any possibility to use the null-conditional operator on MyPage? I'm thinking of something like this:

MyPage?.Data = this.data;

But when I write it like this, I get the following error:

The left-hand side of an assignment must be a variable, property or indexer.

I know it's because MyPage could be null and the left-hand side wouldn't be a variable anymore.

It's not that I cannot use it like I have it already but I just want to know if there's any possibility to use the null-conditional operator on this.

diiN__________
  • 7,393
  • 6
  • 42
  • 69
  • 15
    You should be able to create a `SetData` method and do `MyPage1?.SetData(this.data);` – Joachim Isaksson Mar 09 '16 at 09:06
  • 3
    Possible duplicate of [Why C# 6.0 doesn't let to set properties of a non-null nullable struct when using Null propagation operator?](http://stackoverflow.com/questions/31035022/why-c-sharp-6-0-doesnt-let-to-set-properties-of-a-non-null-nullable-struct-when) – Dovydas Šopa Mar 09 '16 at 09:08
  • 1
    Null propagation/conditional operator is for accessing properties, not setting them. Hence you can't use it. – Tal Avissar Mar 09 '16 at 09:14
  • 6
    I personally think this is a fault in the current implementation. A property on the left side is shorthand for a call to the property setter method so you should be able to use ? on a null property just as if you had explicitly called the set method itself. – Bob Provencher Apr 19 '17 at 15:48
  • 1
    Here is the C# issue: https://github.com/dotnet/csharplang/issues/2883 – user764754 Apr 17 '22 at 10:02

7 Answers7

21

The null propagation operator returns a value. And since you must have a variable on the left hand side of an assignment, and not a value, you cannot use it in this way.

Sure, you could make things shorter by using the tenary operator, but that, on the other hand, doesn't really help the readability aspect.

Joachim Isaksson's comment on your question shows a different approach that should work.

Allmighty
  • 1,499
  • 12
  • 19
14

As Joachim Isaksson suggested in the comments, I now have a method SetData(Data data) and use it like this:

MyPage1?.SetData(this.data);
MyPage2?.SetData(this.data);
MyPage3?.SetData(this.data);
diiN__________
  • 7,393
  • 6
  • 42
  • 69
4

I came up with the following extension,

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

Which may be called globally; this only works on public properties.

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

The following improved version works on anything,

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

And may also be called globally,

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

But the extension name is somewhat misleading as the Action does not exclusively require an assignment.

4

Rather late to the party, but I came to this article with a similar issue. I took the idea of the SetValue method and created a generic extension method, as below:

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

Example usage, for attaching and detaching event an handler:

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

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

Hope somebody finds it useful :)

Paul Williams
  • 95
  • 2
  • 11
1

Try this Add all your pages to myPageList.

IEnumerable<MyPage> myPageList;

foreach(MyPage myPage in myPageList)
{
if (myPage != null)
    myPage.Data = this.data;
}
0

You can Use Extension Method

 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);
liyonghelpme
  • 67
  • 1
  • 5
-1

A generic SetValue extension method (but only for ref properties) would be:

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

And will be used like

ButtonOrNull?.Visibility.SetValue(Visibility.Hidden);
oo_dev
  • 777
  • 7
  • 20
  • 4
    Did you test this? There's absolutely no way this will work... `T property` would have to be a ref parameter for the change to stick because the only thing you are doing is setting the local copy of the property inside the function, and you can't have `this ref` extension methods nor does C# allow ref access to properties (unlike VB, which is a real shame actually). – Mike Marynowski Sep 29 '17 at 15:35
  • Oh, you are very right - it works only with ref parameters (so in my case it worked always ...by accident) – oo_dev Oct 05 '17 at 10:45
  • 1
    @MikeMarynowski: Actually you now can have `ref this` extension methods (but the restriction preventing passing a property to a ref parameter still exists) – Ben Voigt Jul 12 '21 at 21:55
  • @BenVoigt Not for unconstrained `T` though. You can only have `this ref` for value types. – Mike Marynowski Jul 14 '21 at 11:00