MrC
MrC

Reputation: 217

More elegant way of updating empty object properties

I am currently writing a method that fills in the missing properties of an object. The object has had some values set from the database, but if any are empty, then it goes off to an alternative data source (long story).

What this means is that my code has become a little bit like the snippet below

if(string.IsNullOrEmpty(myObject.FieldA))
       myObject.FieldA = UpdateFromMethod("FieldA");
if(string.IsNullOrEmpty(myObject.FieldB))
       myObject.FieldB = UpdateFromMethod("FieldB");
if(string.IsNullOrEmpty(myObject.FieldC))
       myObject.FieldC = UpdateFromMethod("FieldC");

Is this something that I'll just have to live with, or is there a better way of doing this?

Upvotes: 4

Views: 239

Answers (4)

Olivier Jacot-Descombes
Olivier Jacot-Descombes

Reputation: 112372

You could place this logic in the properties

private string _myProp;
public MyProp
{
    get { return _myProp ?? GetValueFromMethod(); }
    set { _myProp = value; }
}

Where the ?? operator is the coalesce operator that yields the value on the right, if the value on the left is null.

Or if you need to test for empty strings as well:

public MyProp
{
    get { return IsNullOrEmpty(_myProp) ? GetValueFromMethod() : _myProp; }
    set { _myProp = value; }
}

You could also place the logic in the setter and initialize the backing variable as well, if it must be set before the setter is called. Advantage over the two first examples: The method is called only once when the getter is called several times.

 private string _myProp = GetValueFromMethod();
 public MyProp
 {
    get { return _myProp; }
    set { _myProp = IsNullOrEmpty(value) ? GetValueFromMethod() : value; }
 }

Placing the logic in both the setter and the getter, as yet anothar alternative, has the advantage that the method is called in a lazy way, compared to calling it in the field initializer and it is called only once as before.

Upvotes: 1

Markus
Markus

Reputation: 22456

You can also use dynamic evaluation of a lambda expression and reflection to set specific properties of the class. This might not be the best solution from a performance point of view, but the the approach works with various data types:

using System;
using System.Linq.Expressions;
using System.Reflection;

public class DataClass
{
    public string Prop1 { get; set; }
    public string Prop2 { get; set; }
    public string Prop3 { get; set; }
    public int Prop4 { get; set; }
    public int? Prop5 { get; set; }
}

public class Test
{
    public static void Main()
    {
        var cls = new DataClass() { Prop1 = null, Prop2 = string.Empty, Prop3 = "Value" };
        UpdateIfNotSet(cls, x => x.Prop1, UpdateMethod);
        UpdateIfNotSet(cls, x => x.Prop2, UpdateMethod);
        UpdateIfNotSet(cls, x => x.Prop3, UpdateMethod);
        UpdateIfNotSet(cls, x => x.Prop4, (x) => -1);
        UpdateIfNotSet(cls, x => x.Prop5, (x) => -1);
        Console.WriteLine(cls.Prop1);  // prints "New Value for Prop1"
        Console.WriteLine(cls.Prop2);  // prints "New Value for Prop2"
        Console.WriteLine(cls.Prop3);  // prints "Value"
        Console.WriteLine(cls.Prop4);  // prints "0"
        Console.WriteLine(cls.Prop5);  // prints "-1"
    }

    public static void UpdateIfNotSet<TOBJ, TPROP>(TOBJ obj, 
                           Expression<Func<TOBJ, TPROP>> prop, 
                           Func<string, TPROP> updateMth)
    {
        var currentValue = prop.Compile().DynamicInvoke(obj);
        var strValue = currentValue as string; // Need to convert to string gracefully so that check against empty is possible
        if (currentValue == null || (strValue != null && strValue.Length == 0))
        {
            var memberAcc = (MemberExpression)prop.Body;
            var propInfo = (PropertyInfo)memberAcc.Member;
            propInfo.SetMethod.Invoke(obj, new object[] { updateMth(propInfo.Name) });
        }
    }

    public static string UpdateMethod(string propName)
    {
        return "New Value for " + propName;
    }
}

The UpdateIfNotSet method expects the following parameters:

  • An object
  • A lambda expression that accesses a property of the object
  • A function that retrieves the updated value. Please note that the function takes a string as a parameter that is filled with the property name when it is called

The generic arguments of the UpdateIfNotSet method are inferred by the compiler, so you do not have to specify them.

The method first compiles the lambda expression and retrieves the current value of the property by invoking the compiled expression. It then checks whether the value is null or an empty string. In this case, it assigns a new value to the property by calling the provided method.

Upvotes: 0

rareyesdev
rareyesdev

Reputation: 2427

Use reflection.

var type = myObject.GetType();

foreach (var field in type.GetFields())
{
    string value = (string)field.GetValue(myObject);

    if (string.IsNullOrEmpty(value))
    {
        UpdateFromMethod(field.Name);
    }
}

Upvotes: 2

Marc Gravell
Marc Gravell

Reputation: 1062865

For that specific type of scenario, the only real alternative to ugly repetitive code would be ugly meta-programming code - and at least the current code is readable. If it was just null you were testing for, null-coalescing (??) might make it tidier, but fundamentally, the code you have works.

If they really are fields (not properties) you could perhaps do something like:

void Update(ref string value, Func<string> source)
{
    if(string.IsNullOrEmpty(value)) value = source();
}
...
Update(ref myObject.FieldA, UpdateFromMethodA);
Update(ref myObject.FieldB, UpdateFromMethodB);
Update(ref myObject.FieldC, UpdateFromMethodC);

but behind the scenes that creates lots of delegate instances that make it undesirable.

Frankly, I'd stick with what you have.

Upvotes: 4

Related Questions