0

I need a clean way to update an object using parameters of same class. I am defining list of fields as Func<T, object> delegates. These are lists that should be compared and updated if nessecary. Unfortunately I can't figure out a clean way to implement it.

Following code doesn't work:

public class UpdatableClass
{
    public int Id { get; set; }
    public int IntValue { get; set; }
    public string StringValue { get; set; }
    public DateTime ModifiedDate { get; set; }


    private List<Func<UpdatableClass, object>> UpdatableFields =
        new List<Func<UpdatableClass, object>>()
        {
            c => c.IntValue,
            c => c.StringValue
        };

    public bool Update(UpdatableClass newValues)
    {
        bool isUpdated = false;
        foreach (var fieldSelector in UpdatableFields)
        {
            object oldValue = fieldSelector(this);
            object newValue = fieldSelector(newValues);
            if (!newValue.Equals(oldValue))
            {
                oldValue = newValue;
                isUpdated = true;
            }
        }

        return isUpdated;
    }
}

[TestFixture]
public class UpdatableClassTests
{
    [Test]
    public void TestUpdateMethod()
    {
        UpdatableClass oldObject = new UpdatableClass()
        {
            StringValue = "OldString",
            IntValue = 3,
        };

        bool isUpdated = oldObject.Update(new UpdatableClass() { StringValue = "NewString", IntValue = 4 });

        Assert.IsTrue(isUpdated);
        Assert.AreEqual("NewString", oldObject.StringValue);
        Assert.AreEqual(4, oldObject.IntValue);
    }
}
  • You're probably better off using a library like AutoMapper that will do this for you. – DavidG Nov 25 '19 at 11:00
  • You `Func` only returns the value, which you've used for comparison then. You'll need to define an Action to set the value of properties. Or write something like in this [thread](https://stackoverflow.com/questions/2823236/creating-a-property-setter-delegate) – Pavel Anikhouski Nov 25 '19 at 11:05
  • The best unit test is the one that fails before you ever run it. Very hard to see the point of this code. – Hans Passant Nov 25 '19 at 11:22
  • @HansPassant, could you please elaborate? I want to be a better question asker :) – Andre Tchernikov Nov 25 '19 at 11:38

1 Answers1

2

This code can be used as possible solution for the problem. Instead of get only Func<T, object> you can use a tuple for both getter and setter (Func<UpdatableClass, object> Get, Action<UpdatableClass, object> Set). I don't think that it's the best solution, but it resolves question and make test passing

public class UpdatableClass
{
    public int Id { get; set; }
    public int IntValue { get; set; }
    public string StringValue { get; set; }
    public DateTime ModifiedDate { get; set; }

    private List<(Func<UpdatableClass, object> Get, Action<UpdatableClass, object> Set)> UpdatableFields =
        new List<(Func<UpdatableClass, object>, Action<UpdatableClass, object>)>
        {
            (c => c.IntValue, (c, v) => { c.IntValue = Convert.ToInt32(v); }),
            (c => c.StringValue, (c, v) => { c.StringValue = v.ToString(); })
        };

    public bool Update(UpdatableClass newValues)
    {
        bool isUpdated = false;
        foreach (var field in UpdatableFields)
        {
            object oldValue = field.Get(this);
            object newValue = field.Get(newValues);
            if (!newValue.Equals(oldValue))
            {
                field.Set(this, newValue);
                isUpdated = true;
            }
        }

        return isUpdated;
    }
}   
Pavel Anikhouski
  • 21,776
  • 12
  • 51
  • 66