简体   繁体   English

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

[英]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? 这是我必须忍受的东西,还是有更好的方法来做到这一点?

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. 如果它只是null你正在测试,null-coalescing( ?? )可能会使它更整洁,但从根本上说,你所使用的代码。

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. 坦率地说,我会坚持你所拥有的。

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

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. 如果左边的值为null,则operator是在右侧产生值的coalesce运算符。

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. 您也可以将逻辑放在setter中并初始化后备变量,如果必须在调用setter之前设置它。 Advantage over the two first examples: The method is called only once when the getter is called several times. 优于前两个示例:只有在多次调用getter时才调用该方法一次。

 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. 将逻辑放置在setter和getter中,作为anothar替代方案,具有以下优点:与在字段初始化程序中调用该方法相比,该方法以惰性方式调用,并且仅像以前一样调用它。

You can also use dynamic evaluation of a lambda expression and reflection to set specific properties of the class. 您还可以使用lambda表达式和反射的动态评估来设置类的特定属性。 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: UpdateIfNotSet方法需要以下参数:

  • An object 一个东西
  • A lambda expression that accesses a property of the object 一个lambda表达式,用于访问对象的属性
  • 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. UpdateIfNotSet方法的泛型参数由编译器推断,因此您不必指定它们。

The method first compiles the lambda expression and retrieves the current value of the property by invoking the compiled expression. 该方法首先编译lambda表达式,并通过调用已编译的表达式来检索该属性的当前值。 It then checks whether the value is null or an empty string. 然后它检查值是null还是空字符串。 In this case, it assigns a new value to the property by calling the provided method. 在这种情况下,它通过调用提供的方法为属性分配新值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM