I finally found a good way to update a set of properties in an API without touching others. Let me explain…
Let’s say I have a method that updates a set of properties on an object. It looks something like this:
class MyProperties { public int PropertyA { get; set; } public int PropertyB { get; set; } } class MyObject { public void UpdateProperties(MyProperties newPropertyValues); }
Callers can use it to update properties A and B as follows:
var newPropertyValues = new MyProperties(); newPropertyValues.PropertyA = 1; newPropertyValues.PropertyB = 2; obj.UpdateProperties(properties);
That works great until you only want to update certain properties. For instance, you might need to update PropertyA but not touch PropertyB. At that point, you have a few options…
First, you could add a new “UpdatePropertyA” method. But this is a slippery slope. As you add more properties, you might also have to add more methods. That can easily turn into a mess. “UpdatePropertyAAndC” anyone?
Second, you could pass flags specifying which properties to set. e.g.,
enum MyPropertiesToSet { UpdatePropertyA = 1, UpdatePropertyB = 2, } class MyObject { public void UpdateProperties(MyProperties newPropertyValues, MyPropertiesToSet propertiesToSet); }
That’s a bit better. But it gets messy because you need to keep the enum in sync with the class.
Third, you can pass “null” for properties that you don’t want to update:
class MyProperties { public int? PropertyA { get; set; } public int? PropertyB { get; set; } }
That actually works fairly well:
var newPropertyValues = new MyProperties(); newPropertyValues.PropertyA = 1; obj.UpdateProperties(newPropertyValues);
I often go with this. But there’s a big problem with it: what if “null” is a valid value for the property? In that case, there’s no way to tell the difference between:
- Don’t change the value.
- Change the value to null.
Ugh. But fear not… I created an OptionalValue class that solves this:
class MyProperties { public OptionalValue<int?> PropertyA { get; set; } public OptionalValue<int?> PropertyB { get; set; } }
Callers can use it the same way as the prior option:
var newPropertyValues = new MyProperties(); newPropertyValues.PropertyA = 1; obj.UpdateProperties(newPropertyValues);
But it also works for null values:
newPropertyValues = new MyProperties(); newPropertyValues.PropertyA = null; obj.UpdateProperties(newPropertyValues);
The code receiving the OptionalValue just needs to check the “HasValue” property to determine if the property should be updated or not:
void UpdateProperties(MyProperties newPropertyValues) { if (newPropertyValues.PropertyA.HasValue) { int? newPropertyValue = newPropertyValues.PropertyA.Value; // Update to the new value } // etc. }
Here’s the source code of OptionalValue, in case you are interested:
public struct OptionalValue<T> { public bool HasValue { get; } private readonly T value; public T Value { get { if (!HasValue) throw new InvalidOperationException("OptionalValue does not have a value"); return value; } } public OptionalValue(T value) { HasValue = true; this.value = value; } public static implicit operator OptionalValue<T>(T value) { return new OptionalValue<T>(value); } public T GetValueOrDefault() { if (HasValue) return value; else return default(T); } public override string ToString() { return HasValue ? (value == null ? "(null)" : value.ToString()) : "(not set)"; } }