Linq Select Where

本文关键字:Where Select Linq | 更新日期: 2023-09-27 18:06:00

我经常发现自己写这样的东西:

var fields = _type.GetProperties()
            .Select(prop => new { Prop = prop, Attrib = prop.GetCustomAttribute<ColumnAttribute>() })
            .Where(t => t.Attrib != null)
            .ToList();

我感到困扰的是,在Where子句失败的情况下,我不必要地创建对象。虽然开销很小,但我仍然希望保存分配,就像我简单地循环它或执行更痛苦的操作一样:

var fields = _type.GetProperties()
        .Select(prop =>
        {
            var attrib = prop.GetCustomAttribute<ColumnAttribute>();
            return attrib == null ? null : new {Prop = prop, Attrib = attrib};
        })
        .Where(t => t != null);

是否有更好的模式/扩展方法我错过了?或者LINQ是否有可能在幕后进行优化?

感谢!

更新:

我想我的意思是这样的,但我希望有类似的东西已经存在,我只是搜索得不好:

public static IEnumerable<TResult> SelectWhereNotNull<TSource, TValue, TResult>(this IEnumerable<TSource> source, Func<TSource, TValue> valueSelector, Func<TSource, TValue, TResult> selector)
    where TValue:class
    where TResult:class
{
    return source
        .Select(s =>
        {
            var val = valueSelector(s);
            if (val == null)
            {
                return null;
            }
            return selector(s, val);
        })
        .Where(r => r != null);
}
var fields = _type.GetProperties()
     .SelectWhereNotNull(prop => prop.GetCustomAttribute<ColumnAttribute>(), Tuple.Create);

Linq Select Where

对于您正在执行的查询类型,您无法真正绕过它。您希望有一个地方放置该属性。无论您是将其隐藏在单独的方法中还是对结果对象进行操作,都必须这样做。担心它只会适得其反。但是有一些方法可以让它更容易读。

如果您在查询语法中重写了查询,则可以隐藏正在执行的事实

var fields =
    from prop in _type.GetProperties()
    let attr = prop.GetCustomAttribute<ColumnAttribute>()
    where attr != null
    select new
    {
        Prop = prop,
        Attrib = attr,
    };
然而,对于这个,我可能会把它打包在一个生成器中。它不需要用LINQ来写,如果你试图这样做,你会严重限制自己。
public static IEnumerable<TResult> SelectWhere<TSource, TValue, TResult>(
        this IEnumerable<TSource> source,
        Func<TSource, TValue> valueSelector,
        Func<TSource, TValue, bool> predicate,
        Func<TSource, TValue, TResult> resultSelector)
{
    foreach (var item in source)
    {
        var value = valueSelector(item);
        if (predicate(item, value))
            yield return resultSelector(item, value);
    }
}

你的查询变成这样:

var fields = _type.GetProperties()
    .SelectWhere(
        p => p.GetCustomAttribute<ColumnAttribute>(),
        (p, a) => a != null,
        (p, a) => new { Prop = p, Attrib = a }
    )
    .ToList();

如果condition为真,可以使用Selectmany: return an one-item array,否则返回Array.Empty

var headers = httpHeaders.SelectMany(pair => allowedHeaders.Contains(pair.Key)
    ? new[] { pair }
    : Array.Empty<KeyValuePair<string, string>>());