寻求非空值对象属性赋值的通用方法(C#场景)
替代逐个if判断的属性赋值优化方案
场景说明:给新创建的对象属性赋值时,仅当第三方提供的待赋值变量通过多维度空值校验(涵盖null、空字符串、空白字符、无效日期等,不同第三方校验逻辑存在差异)时,才覆盖对象默认值。当前实现为逐个属性编写if判断,希望简化调用逻辑,通过传入对象、属性、待赋值变量的方式完成按需赋值。
方案1:通用赋值方法+自定义空值校验器
先定义空值校验接口,针对不同第三方实现专属校验逻辑:
public interface INullChecker { bool IsNullOrInvalid(object value); } // 示例:第三方A的校验器 public class ThirdPartyAChecker : INullChecker { public bool IsNullOrInvalid(object value) { if (value == null) return true; if (value is string str) return string.IsNullOrWhiteSpace(str); if (value is DateTime dt) return dt == DateTime.MinValue; return false; } } // 其他第三方校验器可按相同逻辑实现
再编写通用赋值方法,通过表达式树定位属性,避免硬编码:
public static void AssignIfValid<T, TProp>(T target, Expression<Func<T, TProp>> propertyExpr, TProp newValue, INullChecker checker) { if (checker.IsNullOrInvalid(newValue)) return; var property = (PropertyInfo)((MemberExpression)propertyExpr.Body).Member; property.SetValue(target, newValue); }
调用示例:
var bar = newFoo(); var checker = new ThirdPartyAChecker(); // 根据当前数据源选择对应校验器 AssignIfValid(bar, x => x.ID, newID, checker); AssignIfValid(bar, x => x.Name, newName, checker); AssignIfValid(bar, x => x.OpenDate, newOpenDate, checker);
该方案优势:
- 统一赋值逻辑,避免重复编写if判断
- 第三方校验逻辑封装到单独类中,符合单一职责原则
- 调用语法简洁,意图清晰
方案2:扩展方法增强可读性
将通用赋值方法改为扩展方法,让调用更自然:
public static class ObjectExtensions { public static void AssignIfValid<T, TProp>(this T target, Expression<Func<T, TProp>> propertyExpr, TProp newValue, INullChecker checker) { if (checker.IsNullOrInvalid(newValue)) return; var property = (PropertyInfo)((MemberExpression)propertyExpr.Body).Member; property.SetValue(target, newValue); } }
调用示例:
var bar = newFoo(); var checker = new ThirdPartyAChecker(); bar.AssignIfValid(x => x.ID, newID, checker); bar.AssignIfValid(x => x.Name, newName, checker); bar.AssignIfValid(x => x.OpenDate, newOpenDate, checker);
方案3:批量赋值(适用于属性与第三方值有固定映射的场景)
若第三方字段与内部类属性存在固定对应关系,可通过反射+映射字典批量处理,减少重复代码:
public static void BatchAssign<T>(T target, Dictionary<string, object> thirdPartyValues, INullChecker checker) { var type = typeof(T); foreach (var kvp in thirdPartyValues) { var property = type.GetProperty(kvp.Key); if (property == null) continue; if (checker.IsNullOrInvalid(kvp.Value)) continue; // 可根据需求添加类型转换逻辑,比如string转int等 var convertedValue = Convert.ChangeType(kvp.Value, property.PropertyType); property.SetValue(target, convertedValue); } }
调用示例:
var bar = newFoo(); var thirdPartyData = new Dictionary<string, object> { { nameof(Foo.ID), newID }, { nameof(Foo.Name), newName }, { nameof(Foo.OpenDate), newOpenDate } }; var checker = new ThirdPartyAChecker(); BatchAssign(bar, thirdPartyData, checker);
该方案适合属性数量较多的场景,一次性处理所有属性,但需维护字段映射关系,类型转换需额外处理。
与原生if语句的对比
- 原生if语句:性能最优,无反射/表达式树开销;但代码冗余,属性数量多时需编写大量重复逻辑,后期维护成本高。
- 上述方案:代码简洁,维护性强,校验逻辑统一管理;存在轻微反射性能开销(属性数量在100以内时可忽略)。
若属性数量最多100个且无极端性能要求,方案1或2为更优选择;若性能为首要考量,原生if语句也可接受,虽冗余但稳定。
内容的提问来源于stack exchange,提问作者twhitteberry
相关产品推荐
相关产品推荐

