从对象集合动态构建 lambda 表达式?
Posted
技术标签:
【中文标题】从对象集合动态构建 lambda 表达式?【英文标题】:Dynamically build lambda expression from a collection of objects? 【发布时间】:2018-08-07 17:29:41 【问题描述】:我有一个以这种格式存储的排序列表:
public class ReportSort
public ListSortDirection SortDirection get; set;
public string Member get; set;
我需要把它变成Action<DataSourceSortDescriptorFactory<TModel>>
类型的lambda表达式
所以假设我有以下报告排序集合:
new ReportSort(ListSortDirection.Ascending, "LastName"),
new ReportSort(ListSortDirection.Ascending, "FirstName"),
我需要将其转换为这样的语句才能像这样使用:
.Sort(sort =>
sort.Add("LastName").Ascending();
sort.Add("FirstName").Ascending();
)
排序方法签名是:
public virtual TDataSourceBuilder Sort(Action<DataSourceSortDescriptorFactory<TModel>> configurator)
所以我现在有一些方法:
public static Action<DataSourceSortDescriptorFactory<TModel>> ToGridSortsFromReportSorts<TModel>(List<ReportSort> sorts) where TModel : class
Action<DataSourceSortDescriptorFactory<TModel>> expression;
//stuff I don't know how to do
return expression;
...我不知道在这里做什么。
编辑:答案是:
var expression = new Action<DataSourceSortDescriptorFactory<TModel>>(x =>
foreach (var sort in sorts)
if (sort.SortDirection == System.ComponentModel.ListSortDirection.Ascending)
x.Add(sort.Member).Ascending();
else
x.Add(sort.Member).Descending();
);
起初我在想我必须使用 Expression 类从头开始动态构建一个 lambda 表达式。幸运的是,情况并非如此。
【问题讨论】:
【参考方案1】:...我不知道在这里做什么。
好吧,推理出来。
你手里有什么? List<ReportSort>
称为 sorts
。
你需要什么?一个Action<Whatever>
。
您已经迈出了第一步:您已经创建了一个方法来获取您拥有的东西并返回您需要的东西。伟大的第一步。
Action<DataSourceSortDescriptorFactory<TModel>> expression;
//stuff I don't know how to do
return expression;
而且你已经说出了你不知道该怎么做的事情——但是。这是一个很好的技术。
首先填写一些可以编译但不能正常工作的内容。
Action<DataSourceSortDescriptorFactory<TModel>> expression =
sort =>
sort.Add("LastName").Ascending();
sort.Add("FirstName").Ascending();
;
return expression;
非常好。 现在您有了一个编译程序,这意味着您可以运行您的测试并验证如果这种情况是预期的,则测试通过,如果预期其他任何情况,则测试失败。
现在想想,我手里有什么?我有一个东西清单,我正在做一个Action
。这意味着正在发生副作用,可能涉及列表中的每个项目。所以那里可能有一个foreach
:
Action<DataSourceSortDescriptorFactory<TModel>> expression =
sort =>
sort.Add("LastName").Ascending();
sort.Add("FirstName").Ascending();
foreach(var sort in sorts)
// Do something
;
return expression;
编译它。它失败。啊,我们混淆了我们正在添加的类别和我们正在添加的新类别。解决问题。
Action<DataSourceSortDescriptorFactory<TModel>> expression =
existingSort =>
existingSort.Add("LastName").Ascending();
existingSort.Add("FirstName").Ascending();
foreach(var newSort in sorts)
// Do something
;
return expression;
太好了,现在我们又可以编译和运行测试了。
这里的模式应该很清楚。 继续编译,继续运行测试,逐渐让你的程序越来越正确,推理你可以对你手头的值执行的操作。
你能完成它吗?
【讨论】:
是的,我能够弄清楚,感谢您发布详细的答案。我以为我必须从头开始手动构建一个 lambda 表达式,这就是我试图理解它时头晕目眩的地方,这并不像我担心的那么难。 @SventoryMang:可以完全从头构建一个 lambda 并使用各种技术对其进行编译。但通常没有必要。【参考方案2】:您可以使用可以分配给Action<T>
委托的以下 lambda 表达式。在该 lambda 表达式中,捕获 List<T>
变量并对其进行循环:
public static Action<DataSourceSortDescriptorFactory<TModel>> ToGridSortsFromReportSorts<TModel>(List<ReportSort> sorts) where TModel : class
Action<DataSourceSortDescriptorFactory<TModel>> expression =
result =>
foreach (var sort in sorts)
if (sort.SortDirection == ListSortDirection.Ascending)
result.Add(sort.Member).Ascending();
else // or whatever other methods you want to handle here
result.Add(sort.Member).Descending();
;
return expression;
【讨论】:
请注意,delegate (T t) ...
在 C# 3 发布后编写的代码风格不佳。而是使用(T t) => ...
@EricLippert 是的,我知道我为什么写这个。固定以上是关于从对象集合动态构建 lambda 表达式?的主要内容,如果未能解决你的问题,请参考以下文章
Lambda 表达式 - 根据集合中另一个属性的值设置对象集合中一个属性的值