如何通过Lambda表达式决定要处理的Person类属性?
解决方案
要实现用Lambda指定要裁剪的属性,核心是要同时获取属性的读取、写入能力以及属性名称——普通的Func<Person, string>只能读取值,做不到动态赋值和获取属性名,这里用**表达式树(Expression)**就能完美解决:
实现思路
- 将扩展方法的参数改为
Expression<Func<Person, string>>,通过表达式树解析目标属性的元数据; - 从表达式中提取
PropertyInfo,用它来获取属性名、读取当前值、写入裁剪后的值; - 把裁剪逻辑和日志输出统一封装,完全符合DRY原则。
完整重构代码
public class Person { public string FirstName { get; set; } public string LastName { get; set; } public int Age { get; set; } } public static class PersonExtension { public static void TrimName(this Person person, Expression<Func<Person, string>> propertySelector) { // 解析表达式,提取目标属性的元数据 if (propertySelector.Body is not MemberExpression memberExpr || memberExpr.Member is not System.Reflection.PropertyInfo propInfo) { throw new ArgumentException("请传入有效的Person属性选择表达式"); } var propertyName = propInfo.Name; Console.WriteLine($"Start Trim {propertyName}"); // 安全读取属性值、裁剪后重新赋值 var currentValue = propInfo.GetValue(person) as string; var trimmedValue = currentValue?.Length > 32 ? currentValue.Remove(32) : currentValue; propInfo.SetValue(person, trimmedValue); Console.WriteLine($"End Trim {propertyName}"); } } public class Program { public static void Main() { var person = new Person { FirstName = "Foo", LastName = "Bar" }; // 调用时直接传入属性选择Lambda即可 person.TrimName(x => x.FirstName); person.TrimName(x => x.LastName); } }
补充说明
- 表达式树能自动解析出属性名,不用硬编码,彻底避免重复代码;
- 代码里优化了
Remove(32)的风险:如果原字符串长度小于32,直接保留原内容,不会抛出异常; - 要是不想用表达式树,也可以传递两个委托(一个读、一个写),但调用会繁琐很多,示例如下:
public static void TrimName(this Person person, Func<Person, string> getter, Action<Person, string> setter, string propertyName) { Console.WriteLine($"Start Trim {propertyName}"); var value = getter(person); setter(person, value?.Length >32 ? value.Remove(32) : value); Console.WriteLine($"End Trim {propertyName}"); } // 调用时需要手动传属性名 person.TrimName(p => p.FirstName, (p,v) => p.FirstName=v, nameof(Person.FirstName));
显然表达式树的方案更简洁可靠,是这类场景的首选。
内容的提问来源于stack exchange,提问作者Simon
相关产品推荐
相关产品推荐

