作为参数的 LINQ 表达式

本文关键字:LINQ 表达式 参数 | 更新日期: 2023-09-27 18:34:20

我必须像这样查询集合:

myList.Where(s => myFilters.Contains(s.CountryCode))

s.CountryCode上面是一个例子。我想让它变速并调用不同的列,如下所示:

myList.Where(s => myFilters.Contains(s.City))
myList.Where(s => myFilters.Contains(s.Region))
myList.Where(s => myFilters.Contains(s.Zipcode))

所以我想定义列表达式是参数的函数。这种功能的签名是什么?

public void MySelect( ??? )
{
    myList.Where(s => myFilters.Contains(???);
}

myListObservableCollectionmyFiltersList<string>

作为参数的 LINQ 表达式

将此扩展方法放在静态类中:

public static IEnumerable<T> WhereContains<T, TValue> (this IEnumerable<T> obj, IEnumerable<TValue> container, Func<T, TValue> propertyAccess)
{
    return obj.Where(o => container.Contains(propertyAccess(o)));
}

此扩展方法的工作方式是它接受一个 lambda 函数,该函数在给定您类型的对象的情况下解析属性。所以你只需要把一个简单的兰巴像x => x.City传递给它。

由于它是完全通用的,而不是特定于您的一个myFilters集合,因此您还需要将其传递给函数。但这允许您将此WhereContains用于许多其他情况。

使用它看起来像这样:

// Test is a class with two string properties `City` and `Region`
var myList = new List<Test>{
    new Test() { City = "asdf", Region = "fdsa" },
    new Test() { City = "foo", Region = "bar" },
    new Test() { City = "bar", Region = "baz" }
};
var myFilters = new List<string> { "asdf", "bar" };
myList.WhereContains(myFilters, x => x.City); // contains asdf/fdsa and bar/baz
myList.WhereContains(myFilters, x => x.Region); // contains foo/bar

您可以使用反射

 public void MySelect(string column)
 {
   var result  = myList.Where(s => myFilters.Contains(s.GetType().GetProperty(column)));
 }

作为替代解决方案,您可以使用以下方法,该方法允许指定过滤器字段或过滤器功能:

var adresses = new List<Address>{
    new Address() { City = "ABC", Country = "USA" },
    new Address() { City = "BC", Country = "USA" },
    new Address() { City = "C", Country = "UK" }
};
var filterValues = new List<string> { "B", "UK", "U" };
//
var FilterContains = filterValues.@Specify((values, value) => values.Contains(value));
var FilterStartsWith = filterValues.@Specify((values, value) => values.Any(v => value.StartsWith(v)));
//
var AdressesByCity = adresses.@Specify(a => a.City);
var adressesByCitiesContains = AdressesByCity(filterValues, FilterContains); // B->{ABC;USA},{BC;USA}
var adressesByCitiesStartsWith = AdressesByCity(filterValues, FilterStartsWith);// B->{BC;USA}
//
var AdressesByCountry = adresses.@Specify(a => a.Country);
var adressesByCountriesContains = AdressesByCountry(filterValues, FilterContains);//U,UK-> {C;UK}
var adressesByCountriesStartsWith = AdressesByCountry(filterValues, FilterStartsWith); //U,UK->{ABC;USA},{BC;USA}{C;UK}

这里@Specify扩展方法

实现如下:
public static class @SpecifyExtension {
    public static Func<IEnumerable<V>, Func<IEnumerable<V>, V, bool>, IEnumerable<U>> @Specify<U, V>(this IEnumerable<U> source, Func<U, V> selector) {
        return (values, predicate) => source.Where(x => predicate(values, selector(x)));
    }
    public static Func<IEnumerable<TValue>, TValue, bool> @Specify<TValue>(this IEnumerable<TValue> source, Func<IEnumerable<TValue>, TValue, bool> func) {
        return func; // for Type-inference only
    }
}