1

I want to write a method that has optional parameters. It should update some object with only those parameters that were given to it while leaving other properties unchanged.

The problematic part:

  • Null is a valid value for all object properties
  • The value of object properties can not be read to compare with

What should the signature of the method be and what should the logic of the method be?

So lets say the object looks like this:

public class Foo
{
    public int Id { get; set; }
    public bool? MyBool { get; set; }
    public int? MyInt { get; set; }
    public string MyString { get; set; }
}

And lets say the method looks like this:

public void UpdateObject(int id, bool? optBool = null, int? optInt = null,
                          string optString = null)
{
    var objectToUpdate = _fooList.FirstOrDefault(x => x.Id = id);

    if(objectToUpdate != null)
    {
        // Update only set properties?
    }
}

Now, I want to call that method to update properties in different parts of my application like this:

// at some part of app
UpdateObject(1, optInt: 5);
// will set the int property to 5 and leaves other properties unchanged

// at other part of app
UpdateObject(1, optString: "lorem ipsum");
// will set the string property and leaves other properties unchanged

// at other part of app
UpdateObject(1, optBool: null, optString: "lorem ipsum");
// will set the string and bool property and leaves other properties unchanged

Please note that just setting values will not work because it will overwrite unwanted properties with null.

public void UpdateObject(int id, bool? optBool = null, int? optInt = null,
                          string optString = null)
{
    var objectToUpdate = _fooList.FirstOrDefault(x => x.Id = id);

    if(objectToUpdate != null)
    {
        // This is wrong
        objectToUpdate.MyBool = optBool;
        objectToUpdate.MyInt = optInt;
        objectToUpdate.MyString = optString;
    }
}
Tadija Bagarić
  • 2,495
  • 2
  • 31
  • 48
  • 4
    If `null` is a valid value, you need to wrap each value with a bool indicating it should be written, something like `new UpdateableProp { Name = "optBool", Value = null }`. Or add a bool parameter per parameter, indicating the same. – CodeCaster Jul 31 '17 at 13:17
  • You can use single property of enum with [`[Flags]`](https://stackoverflow.com/q/8447/1997232) type to *code* which properties has to be serialized. – Sinatr Jul 31 '17 at 13:23

2 Answers2

3

Instead of passing in the new value. Pass in a Func<T> that supplies the new value. If the Func is null, then you don't do anything. You only set the value to null if the Func returns null.

public void UpdateObject(Func<int> idProvider, Func<bool?> optBoolProvider = null, Func<int?> optIntProvider = null,
                          Func<string> optStringProvider = null)
{
    if(idProvider != null) Id = idProvider(); // etc.
}

And you call it like:

UpdateObject(() => 1234, () => null, optStringProvider: () => "hello world");

An alternative, if you can read the current value, is a Func<T,T> and instead of defaulting to null you default to identity, i.e. x -> x. Then you don't need to do the null check (except as a contract if you must)

    public void UpdateObject(Func<int,int> idProvider, Func<bool?,bool?> optBoolProvider = x => x, Func<int?> optIntProvider = x => x,
                              Func<string> optStringProvider = x => x)
    {
        Id = idProvider(Id); // etc.
    }

I've been in java land lately, so apologies if the syntax is off.

Novaterata
  • 4,356
  • 3
  • 29
  • 51
0

You could overload the functions, giving you options as to which vars to add and which to keep the same.

This way you have the option to only change values of the given object you wish to change.

You can also do the following:

public Foo changeString(Foo f, string s) 
{
    f.myString = s;
    return f;
}
public Foo changeInt(Foo f, int i)
{
    f.myInt = i;
    return f;
}

//external piece of code
Foo f = new Foo ();
f = changeInt(f, 5).changeString(f, "abc");

this will chain the functions and edit both properties without touching anything else. Maybe this can help you with your problem.

Bryan
  • 91
  • 8