我目前正在编写一个填充对象缺失属性的方法。该对象已从数据库中设置了一些值,但如果有任何值为空,则它将转到备用数据源(长篇故事)。
这意味着我的代码有点像下面的代码段
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");
这是我必须忍受的东西,还是有更好的方法呢?
答案 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
方法需要以下参数:
UpdateIfNotSet
方法的泛型参数由编译器推断,因此您不必指定它们。
该方法首先编译lambda表达式,并通过调用已编译的表达式来检索该属性的当前值。然后它检查值是null还是空字符串。在这种情况下,它通过调用提供的方法为属性分配新值。