Using the null-conditional operator on the left-hand side of an assignment
Asked Answered
A

7

46

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.

Aurelioaurelius answered 9/3, 2016 at 9:3 Comment(5)
You should be able to create a SetData method and do MyPage1?.SetData(this.data);Ladonna
Possible duplicate of Why C# 6.0 doesn't let to set properties of a non-null nullable struct when using Null propagation operator?Footage
Null propagation/conditional operator is for accessing properties, not setting them. Hence you can't use it.Bodega
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.Amoebic
Here is the C# issue: github.com/dotnet/csharplang/issues/2883Declamation
A
23

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 ternary 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.

Alwitt answered 9/3, 2016 at 9:11 Comment(1)
Except you don’t have “a variable” on the left-hand side of a property assignment either, do you? A property assignment x.Y = z is basically just a syntax sugar for a method call like x.SetY(z); if C# allowed using an explicit setter call, I could use x?.SetY(z), but this is not allowed.Ridglee
A
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);
Aurelioaurelius answered 9/3, 2016 at 9:46 Comment(0)
L
5

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.

Lenz answered 23/1, 2018 at 19:51 Comment(0)
O
5

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 :)

Ojibwa answered 5/10, 2018 at 0:52 Comment(0)
F
2

Try this Add all your pages to myPageList.

IEnumerable<MyPage> myPageList;

foreach(MyPage myPage in myPageList)
{
if (myPage != null)
    myPage.Data = this.data;
}
Frye answered 9/3, 2016 at 9:15 Comment(0)
C
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);
Cageling answered 29/9, 2020 at 8:19 Comment(0)
B
-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);
Baryton answered 29/5, 2017 at 11:18 Comment(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).Nappie
Oh, you are very right 👍 - it works only with ref parameters (so in my case it worked always ...by accident)Baryton
@MikeMarynowski: Actually you now can have ref this extension methods (but the restriction preventing passing a property to a ref parameter still exists)Riyal
@BenVoigt Not for unconstrained T though. You can only have this ref for value types.Nappie

© 2022 - 2024 — McMap. All rights reserved.