Reputation: 217
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
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
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:
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
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
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