我正在尝试使用lambda函数的组合创建动态过滤器。但lambda函数的数量可能会根据用户应用的不同过滤器的数量而有所不同。
我想要一些表现得像这样的东西
//images is a List<ImageInfo>
var result1 = images
.Where(image => image.Bytes < 1000)
.Where(image => image.Height < 100)
.Where(image => image.Width < 100);
高度过滤器仅应用于通过字节过滤器的图像。宽度过滤器仅适用于通过高度过滤器的图像。
但是,用户如何打开和关闭过滤器的动态方式不允许我创建一个lambda函数。
相反,我将创建一个lambda函数列表,然后需要将它们应用于图像列表。所以我最终得到这样的东西;多个单独的lambda函数。
var filter1 = images.Where(image => image.Bytes < 1000);
var filter2 = images.Where(image => image.Height < 100);
var filter3 = images.Where(image => image.Width < 100);
如何将多个lambda函数连接在一起以获取最终的过滤图像列表?
我做了这个
var result = filter1.Intersect<ImageInfo>(filter2).Intersect<ImageInfo>(filter3);
但是每个滤镜都会旋转主要的图像列表以获取其图像子集,然后进行交叉计算,这会占用过多的CPU。
所以我正在寻找的是一种方法来获取lambda函数的任意列表(表达式...无论如何)并以一种方式加入它们,这种方式可以像我展示的第一个例子那样执行。
答案 0 :(得分:3)
好的,怎么样:
static Func<T, bool> CombineWithAnd<T>(IEnumerable<Func<T, bool>> filters)
{
return x =>
{
foreach (var filter in filters)
{
if (!filter(x))
{
return false;
}
}
return true;
};
}
这就是你要追求的吗?基本上,它捕获lambda表达式中的一组过滤器,并在使用返回的lambda时一个接一个地应用它们(带短路)。显然你也可以用“或”的方式做同样的事情:
static Func<T, bool> CombineWithOr<T>(IEnumerable<Func<T, bool>> filters)
{
return x =>
{
foreach (var filter in filters)
{
if (filter(x))
{
return true;
}
}
return false;
};
}
请注意,如果在调用方法后修改过滤器集合(例如,它是List<T>
并添加新过滤器),则返回的“组合”过滤器将反映这些更改。如果您不想要这种行为,请添加:
filters = filters.ToList();
作为方法的第一行,有效地复制一份。请注意,委托是不可变的,因此您无需担心他们更改。
答案 1 :(得分:1)
return true
需要移出foreach循环(如下所示)。但是,是的,这正是我正在寻找的。 p>
一个问题或评论。真正让我觉得这个函数是x变量。我必须运行并调试代码,以确定x将是<T>
类型。我以前从来没有见过一个没有类型或var声明的变量,这真的让我失望了。你能解释一下C#允许变量x没有任何声明的规则吗?
顺便提一下非常优雅的解决方案。
static Func<T, bool> CombineWithAnd<T>(IEnumerable<Func<T, bool>> filters)
{
return x =>
{
foreach (var filter in filters)
{
if (!filter(x))
{
return false;
}
}
return true;
};
}
答案 2 :(得分:0)
为什么不一个接一个地应用这些功能,如第一个例子?
您的过滤器具有
的签名Func<IEnumerable<ImageInfo>, IEnumerable<ImageInfo>>
那么只需将每个过滤器应用于最后一个过滤器的结果吗?
像这样?IEnumerable<ImageInfo> filtered = images;
if(filterByBytes)
filtered = filtered.Where(image => image.Bytes < 1000);
if(filterByHeight)
filtered = filtered.Where(image => image.Height < 100);
if(filterByWidth)
filtered = filtered.Where(image => image.Width < 100);
编辑重新:评论,在我的头顶,像... ...
List<Func<IEnumerable<ImageInfo>, IEnumerable<ImageInfo>>> lambdas = new List<Func<IEnumerable<ImageInfo>, IEnumerable<ImageInfo>>>();
lambdas.add(x => x.Where(image => image.Bytes < 1000));
lambdas.add(x => x.Where(image => image.Height < 100));
lambdas.add(x => x.Where(image => image.Width < 100));
foreach(var lambda in lambdas)
images = lamdba.Invoke(images);
答案 3 :(得分:0)
对于它的价值,你不需要使用循环。 (在Python中,因为我没有使用过C#:)
def conjoin(filter1, filter2):
return lambda x: filter1(x) and filter2(x)
def conjoin_list(filters):
return reduce(conjoin, filters)
(Equivalent of the reduce function in C#, called fold there.)