如何在 LINQ 表达式生成器中使用值数组?

Jay*_*man 6 c# linq expression

我想动态构建一个 LINQ 查询,这样我就可以做类似的事情

var list = n.Elements().Where(getQuery("a", "b"));
Run Code Online (Sandbox Code Playgroud)

代替

var list = n.Elements().Where(e => e.Name = new "a" || e.Name == "c");
Run Code Online (Sandbox Code Playgroud)

(大多数时候,我需要传递带有命名空间的 XName,而不仅仅是本地名称......)

我的问题是访问数组元素:

private static Func<XElement, bool> getQuery(XName[] names)
{
    var param = Expression.Parameter(typeof(XElement), "e");

    Expression exp = Expression.Constant(false);
    for (int i = 0; i < names.Length; i++)
    {
         Expression eq = Expression.Equal(
         Expression.Property(param, typeof(XElement).GetProperty("Name")!.Name),
         /*--->*/ Expression.Variable(names[i].GetType(), "names[i]")
         );
    }
    var lambda = Expression.Lambda<Func<XElement, bool>>(exp, param);

    return lambda.Compile();
}
Run Code Online (Sandbox Code Playgroud)

显然变量表达式是错误的,但我很难构建能够访问数组值的表达式。

Sco*_*nen 1

您需要创建表达式并编译它吗?除非我错过了一些细微差别,否则您所需要的只是一个返回Func<XElement, bool>.

private Func<XElement, bool> GetQuery(params string[] names)
{
    return element => names.Any(n => element.Name == n);
}
Run Code Online (Sandbox Code Playgroud)

这需要一个字符串数组并返回一个Func<XElement>. 如果元素名称与任何参数匹配,该函数将返回 true。

然后您可以按照您的描述使用它:

var list = n.Elements.Where(GetQuery("a", "b"));
Run Code Online (Sandbox Code Playgroud)

有很多方法可以做这样的事情。为了提高可读性,像这样的扩展可能会更好:

public static class XElementExtensions
{
    public static IEnumerable<XElement> WhereNamesMatch(
        this IEnumerable<XElement> elements, 
        params string[] names)
    {
        return elements.Where(element => 
            names.Any(n => element.Name == n));
    }
}
Run Code Online (Sandbox Code Playgroud)

那么使用它的代码就变成了

var list = n.Elements.WhereNamesMatch("a", "b");
Run Code Online (Sandbox Code Playgroud)

当 LINQ 查询中有其他筛选器时,这尤其有用。所有方法Where和其他方法都会变得难以阅读。但是,如果我们将它们隔离到具有清晰名称的自己的函数中,那么用法就更容易阅读,并且我们可以在不同的查询中重复使用该扩展。