在 C# 中调用具有表达式作为其参数之一的扩展方法

本文关键字:参数 方法 扩展 调用 表达式 | 更新日期: 2023-09-27 18:36:08

请考虑以下语句:

recorder.AddActivity(new Activity { ActivityName = "DeepSeaDiving", DayOfWeek = DayOfWeek.Monday });

取而代之的是,这里有一个帖子,用Expression Trees像这样花哨地打电话:

 new WeeklyActivityRecorder () .WithActivities( Monday => "Lawn Moving",Tuesday => "Cooking");

我在这里看到了扩展方法,如下所示。

public static WeeklyActivityRecorder WithActivities(this WeeklyActivityRecorder recorder, params Expression<Func<DayOfWeek, string>>[] activityList) 
    {
    foreach (var activity in activityList)
                {
                    LambdaExpression expression = activity;
                    ConstantExpression enteredActivity = expression.Body as ConstantExpression;
                    DayOfWeek day = expression.Parameters[0];
                    recorder.AddActivity(new Activity{DayOfWeek = day, ActivityName = activity});
                }
                return recorder;
    }

但是,当我编译它时,编译器对扩展方法不满意,并抱怨"无法将sourceType ParameterExpression转换为DayOfWeek

上面使用的类的相关代码如下:

public class Activity
{
    /// <summary>
    /// Gets the activity name.
    /// </summary>
    public string ActivityName { get; set; }
    /// <summary>
    /// Gets The day of week.
    /// </summary>
    public DayOfWeek DayOfWeek { get; set; }
}
public class WeeklyActivityRecorder
{
    private List<Activity> Activities { get; set; }
    public WeeklyActivityRecorder()
    {
    }
    public List<Activity> GetAllActivities()
    {
        return this.Activities;
    }
    public void AddActivity(Activity activity)
    {
        if (this.Activities == null)
        {
            this.Activities = new List<Activity>();
        }
        if (activity != null)
        {
            this.Activities.Add(activity);
        }
    }
}

知道我在这里错过了什么吗?

在 C# 中调用具有表达式作为其参数之一的扩展方法

那篇博文中的代码甚至没有编译,无论如何我都不会使用它。表达式树会产生很大的开销,无论如何,将对象初始值设定项用于此目的更具可读性。

为什么它不起作用

当你编写一个lambda时,即使是作为一个表达式,左边是lambda的参数 - 它们的类型和名称,而不是它们的值。因此,代码中的MondayTuesday只是参数名称的文字。编译器不会验证它们是否为有效的枚举成员。

较短的语法选项

Activity类使用构造函数参数:

recorder.AddActivities(new Activity("DeepSeaDiving", DayOfWeek.Monday),
                       new Activity("Lawn Mowing", DayOfWeek.Tuesday));

还可以对 WeeklyActivityRecorder 类使用集合初始值设定项:

class WeeklyActivityRecorder : IEnumerable
{
   public void Add(string name, DayOfWeek day) { ... }
   IEnumerator IEnumerable.GetEnumerator() { return Activities.GetEnumerator(); }
}
var recorder = new WeeklyActivityRecorder {
    { "DeepSeaDiving", DayOfWeek.Monday },
    { "Lawn Mowing", DayOfWeek.Tuesday} };

我不是100%确定那篇文章在做什么......但我有一种感觉,你做错了。

但是,您可以按照我认为的意图使其工作:

DayOfWeek day = (DayOfWeek)Enum.Parse(typeof(DayOfWeek), activity.Parameters[0].Name);

您应该在每次迭代参数名称处存储activity。这会将其解析为枚举类型。

此外,还应存储活动名称的表达式正文:

ActivityName = activity.Body.ToString()

允许此操作:

activityRecorder.WithActivities(Monday => "Something", Tuesday => "Something");

这太笨拙了......所以我肯定会重新考虑你想做什么。如果您正在尝试创建一个表达式生成器(如本文所示),那么您应该只构建表达式并将它们直接传递给 NHibernate。

如果你不这样做......那么你真的应该研究正确地走动表达式并决定你想用它们做什么。

没有任何关于你希望最终使用它的上下文......这是我能做的最好的事情(其他人可能有更好的想法......如果是这样的话,我会很乐意删除我的答案)。

您提供的代码在很多层面上都是错误的。

首先,在这一行中没有从 ParameterExpression 到 DayOfWeek 枚举的直接隐式强制转换:

DayOfWeek day = expression.Parameters[0];

这就是为什么你得到提到的编译器错误。

其次,WithActivities扩展方法的整个想法是错误的。不能从表达式的参数中提取运行时信息。您传递的确切值是运行时信息。

第三,不能使用如下所示的星期一星期二名称,直到它们是调用者类的成员。 否则,它们只是参数的名称,你对它们没有任何智能感知支持。

因此,使用表达式树的全部意义超出了我的理解。一个接近的东西,不涉及表达式树,但提供一个流畅的接口,看起来像:

public static WeeklyActivityRecorder WithActivity(this WeeklyActivityRecorder recorder, DayOfWeek day, string activity)
{
    recorder.AddActivity(new Activity {ActivityName = activity, DayOfWeek = day});
    return recorder;
}

和用法:

new WeeklyActivityRecorder()
    .WithActivity(DayOfWeek.Monday, "Lawn Moving")
    .WithActivity(DayOfWeek.Tuesday, "Cooking");