上下文:我们有以下代码示例:

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 TrimFirstName(Person person)
    {
        Console.WriteLine($"Start Trim {nameof(person.FirstName)}");
        person.FirstName = person.FirstName.Remove(32);
        Console.WriteLine($"End Trim {nameof(person.FirstName)}");
    }
    
    public static void TrimLastName(Person person)
    {
        Console.WriteLine($"Start Trim {nameof(person.LastName)}");
        person.LastName = person.LastName.Remove(32);
        Console.WriteLine($"End Trim {nameof(person.LastName)}");
    }
}

public class Program
{
    public static void Main()
    {
        var person = new Person { FirstName = "Foo", LastName = "Bar" };
        
        PersonExtension.TrimFirstName(person);
        PersonExtension.TrimLastName(person);
    }
}

目标:我们希望重构代码,并将函数TrimFirstNameTrimLastName转换为单个函数,以使其更加枯燥.

我们目前停留在lambda表达式上,试图解决这个问题:

public static class PersonExtension
{   
    public static void TrimName(this Person person, Func<Person, string> action)
    {
        // Console.WriteLine($"Start Trim {nameof(person.LastName)}"); // ??
        person.LastName = action(person).Remove(32); // how to assign to LastName/FirstName ???
        // Console.WriteLine($"End Trim {nameof(person.LastName)}"); // ??

        // more code here that uses person.<propertyName>
    }
}

public class Program
{
    public static void Main()
    {
        var person = new Person { FirstName = "Foo", LastName = "Bar" };
        
        person.TrimName(x => x.FirstName);
    }
}

问:我们如何使用lambda表达式来决定应该修剪哪些属性?

推荐答案

如果您只想获取属性的值并获取其名称,则可以使用一个表达式树(类似于my answer here):

public static void TrimName(this Person person, Expression<Func<Person, string>> propertySelector)
{
    if (propertySelector.Body is MemberExpression memberAccess) {
        var getter = propertySelector.Compile();
        var propertyValue = getter(person);
        var propertyName = memberAccess.Member.Name;
    } else {
        // lambda is not a member access
    }
}

但是,如果您还想设置该属性,我想不出比使用反射调用PropertyInfo.SetValue更好的方法了:

public static void TrimName(this Person person, Expression<Func<Person, string>> propertySelector)
{
    if (propertySelector.Body is MemberExpression memberAccess &&
        memberAccess.Member is PropertyInfo propertyInfo &&
        propertyInfo.CanWrite) {
        var getter = propertySelector.Compile();

        Console.WriteLine($"Start Trim {memberAccess.Member.Name}");
        propertyInfo.SetValue(person, getter(person).Remove(32));
        Console.WriteLine($"End Trim {memberAccess.Member.Name}");
    } else {
        // lambda is not a settable property
    }
}

另一种 Select 是传入另一个专门设置该属性的lambda,将用法转换为:

person.TrimName(x => x.LastName, (x, name) => x.LastName = name);

Csharp相关问答推荐

MongoDB实体框架核心:表达必须可写

使用其可能实现的基类和接口的属性的方法

ListaryImportProperty的默认DllImportSearchPathsProperty行为

Unity如何在PlayerPrefs中保存数据?

返回TyedResults.BadRequest<;字符串>;时问题详细信息不起作用

静态对象构造顺序

在路由中使用枚举

无法通过绑定禁用条目

UWP中的任务和界面

在swagger示例中添加默认数组列表

如何在C#中转换泛型包装类内部的派生类

当我没有此令牌时,为什么语法报告EOF错误?

同一组件的多个实例触发相同的事件处理程序

.Net MAUI,在将FlyoutPage添加到容器之前,必须设置添加构造函数导致Flyout和Detail"

如何从Entity Framework Core中填充ListIInterface

如何使用ODP.NET C#设置Oracle会话时间长度限制

如何在.NET Core 8中自定义标识用户模型

S,在.NET核心控制台应用程序中,AddScope和AddSingleton有什么不同?

这是T自身的布尔表达式是什么意思?

C#如何替换两个XML元素值?