C# Linq选择在哪里

C# Linq选择在哪里,c#,linq,linq-to-objects,C#,Linq,Linq To Objects,我经常发现自己在写这样的东西: var fields = _type.GetProperties() .Select(prop => new { Prop = prop, Attrib = prop.GetCustomAttribute<ColumnAttribute>() }) .Where(t => t.Attrib != null) .ToList(); var字段=\u type.GetPr

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

var fields = _type.GetProperties()
            .Select(prop => new { Prop = prop, Attrib = prop.GetCustomAttribute<ColumnAttribute>() })
            .Where(t => t.Attrib != null)
            .ToList();
var字段=\u type.GetProperties()
.Select(prop=>new{prop=prop,Attrib=prop.GetCustomAttribute()})
.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);
var字段=\u type.GetProperties()
.选择(prop=>
{
var attrib=prop.GetCustomAttribute();
返回attrib==null?null:new{Prop=Prop,attrib=attrib};
})
。其中(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);
公共静态IEnumerable SelectWhereNotNull(此IEnumerable源、Func valueSelector、Func selector)
其中TValue:类
结果:在哪里上课
{
返回源
。选择(s=>
{
var val=值选择器;
if(val==null)
{
返回null;
}
返回选择器(s、val);
})
。其中(r=>r!=null);
}
变量字段=_type.GetProperties()
.SelectWhereNotNull(prop=>prop.GetCustomAttribute(),Tuple.Create);

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

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

var fields =
    from prop in _type.GetProperties()
    let attr = prop.GetCustomAttribute<ColumnAttribute>()
    where attr != null
    select new
    {
        Prop = prop,
        Attrib = attr,
    };
var字段=
来自_type.GetProperties()中的prop
让attr=prop.GetCustomAttribute()
attr在哪里!=无效的
选择新的
{
道具=道具,
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);
    }
}
公共静态IEnumerable SelectWhere(
这是一个数不清的来源,
Func值选择器,
Func谓词,
Func结果选择器)
{
foreach(源中的var项)
{
var值=值选择器(项目);
if(谓词(项、值))
收益返回结果选择器(项目、值);
}
}
您的查询变为:

var fields = _type.GetProperties()
    .SelectWhere(
        p => p.GetCustomAttribute<ColumnAttribute>(),
        (p, a) => a != null,
        (p, a) => new { Prop = p, Attrib = a }
    )
    .ToList();
var字段=\u type.GetProperties()
.选择地点(
p=>p.GetCustomAttribute(),
(p,a)=>a!=null,
(p,a)=>new{Prop=p,Attrib=a}
)
.ToList();

为什么要在筛选之前进行投影?如果您担心投影中不必要的分配,那么在使用
Select()
?@ChrisHardie进行投影之前,您是否可以使用
Where.(prop=>prop.GetCustomAttribute()!=null)
进行过滤?这也是我的第一个想法。但是,调用两次
prop.GetCustomAttribute
可能比分配两次更糟糕。(直觉,这里没有真实的数据)定制LINQ操作符功能强大,不幸的是太少了。这是一个非常优雅的例子。当你说隐藏正在进行的事实时,你的意思是在它为每个元素分配的掩护下,即使attr!=null失败了?无论哪种方式,这肯定更优雅-非常感谢,先生@吉恩:对。新变量必须放在某个地方,以便所有变量都可以在范围内。无论是在闭包还是结果对象中,都必须完成。因此,您可以选择显式执行,也可以通过让编译器处理隐式执行。@Jeff:Gotcha,这很有意义。我真的应该花更多的时间在反编译器/源代码中,以充分理解细节。再次感谢您的经验,先生!