jeudi 2 décembre 2021

Set a property given a lambda expression for that property

I'm looking to create a slick helper function that compares two values, returns true if they are different, and also updates the first value to equal the second value. This is what I came up with:

bool UpdateHelper<T>(ref T originalProperty, T newProperty) =>
    !Equals(originalProperty, newProperty) && (originalProperty = newProperty) is T _;

bool changes = false;
changes |= UpdateHelper(ref name, frmName.Text);
changes |= UpdateHelper(ref description, frmDescription.Text);
...

The goal is that when doing this for dozens of properties, to avoid copy-pasting the same 5 lines of code for each property:

if (myModel.SomeProperty != someUserInput.Text)
{
   myModel.SomeProperty = someUserInput.Text
   changes = true
}

(Especially after having seen someone copy paste and update the if statement portion but forgetting to update the assignment!)

My above helper works for for the most part, but unfortunately this falls apart when I try to use it on properties:

changes |= UpdateHelper(ref myModel.Name, frmName.Text);
changes |= UpdateHelper(ref myModel.Description, frmDescription.Text);

Property access returns temporary value. 'ref' argument must be an assignable variable, field or an array element

Can anyone think of a solution that would work for both?


I tried playing around with expressions, e.g.:

bool UpdateHelper<T>(Expression<Func<T>> originalProperty, T newProperty) =>
    !Equals(originalProperty.Compile().Invoke(), newProperty) && (originalProperty.Body.??? = newProperty) is T _;

changes |= UpdateHelper(() => myModel.Name, frmName.Text);
changes |= UpdateHelper(() => myModel.Description, frmDescription.Text);
changes |= UpdateHelper(() => myModel.ChildObject.Name, frmChildName.Text);

but I feel like it's going to turn into a nightmare of parsing expression trees, especially if it needs to handle a special case of accessing properties nested a few layers down (like the last example above).

Anyone ideas for how to do this elegantly?





Aucun commentaire:

Enregistrer un commentaire