更新空对象属性的更优雅的方式
本文关键字:方式 更新 对象 属性 | 更新日期: 2023-09-27 17:51:14
我目前正在编写一个方法来填充对象的缺失属性。对象已经从数据库中设置了一些值,但是如果有任何值是空的,那么它将转到另一个数据源(说来话长)。
这意味着我的代码变得有点像下面的代码片段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");
这是我不得不忍受的,还是有更好的方法?
对于这种特定的类型的场景,唯一真正替代丑陋的重复代码的是丑陋的元编程代码——至少当前的代码是可读的。如果您要测试的只是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);
,但在幕后,它创建了许多不受欢迎的委托实例。
坦率地说,我会坚持你的。
使用反射
var type = myObject.GetType();
foreach (var field in type.GetFields())
{
string value = (string)field.GetValue(myObject);
if (string.IsNullOrEmpty(value))
{
UpdateFromMethod(field.Name);
}
}
您可以将此逻辑放在属性
private string _myProp;
public MyProp
{
get { return _myProp ?? GetValueFromMethod(); }
set { _myProp = value; }
}
如果左边的值为空,则??
运算符是产生右边值的合并运算符。
或者如果你也需要测试空字符串:
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中,作为另一种替代方法,与在字段初始化器中调用方法相比,它具有以惰性方式调用方法的优点,并且像以前一样只调用一次。
还可以使用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表达式,并通过调用编译后的表达式来检索属性的当前值。然后检查该值是空还是空字符串。在本例中,它通过调用提供的方法为属性分配一个新值。