更优雅的更新空对象属性的方法

时间:2014-03-21 20:50:34

标签: c# oop

我目前正在编写一个填充对象缺失属性的方法。该对象已从数据库中设置了一些值,但如果有任何值为空,则它将转到备用数据源(长篇故事)。

这意味着我的代码有点像下面的代码段

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");

这是我必须忍受的东西,还是有更好的方法呢?

4 个答案:

答案 0 :(得分:4)

对于特定的类型的场景,丑陋的重复代码的唯一真正替代方案是丑陋的元编程代码 - 至少当前代码是可读的。如果它只是null你正在测试,null-coalescing(??)可能会使它更整洁,但从根本上说,你所使用的代码。

如果它们确实是字段(不是属性),那么可以执行以下操作:

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);

但幕后创建了许多委托实例,使其不受欢迎。

坦率地说,我会坚持你拥有的东西。

答案 1 :(得分:2)

使用反射。

var type = myObject.GetType();

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

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

答案 2 :(得分:1)

您可以将此逻辑放在属性

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

如果??运算符是在右侧产生值的coalesce运算符,则左边的值为null。

或者如果您还需要测试空字符串:

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

如果必须在调用setter之前设置逻辑,也可以将逻辑放在setter中并初始化后备变量。优于两个第一个例子:当多次调用getter时,该方法只被调用一次。

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

将逻辑放在setter和getter中,作为anothar的替代方案,与在字段初始化程序中调用方法相比,该方法以惰性方式调用,并且只调用一次。< / p>

答案 3 :(得分:0)

您还可以使用lambda表达式和反射的动态评估来设置类的特定属性。从性能的角度来看,这可能不是最佳解决方案,但该方法适用于各种数据类型:

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;
    }
}

UpdateIfNotSet方法需要以下参数:

  • 一个对象
  • 访问对象属性的lambda表达式
  • 检索更新值的函数。请注意,该函数将字符串作为参数,在调用
  • 时填充属性名称

UpdateIfNotSet方法的泛型参数由编译器推断,因此您不必指定它们。

该方法首先编译lambda表达式,并通过调用已编译的表达式来检索该属性的当前值。然后它检查值是null还是空字符串。在这种情况下,它通过调用提供的方法为属性分配新值。