Linq Select Where
For the type of query you're performing, you can't really get around it. You will want to have a place to put that attribute somewhere. Whether you hide it in a separate method or operate on your result object, it has to be done. It would be counter productive to worry about it. But there are ways you can make it more readable.
If you rewrote your query in the query syntax, you can hide the fact that it is being done
var fields =
from prop in _type.GetProperties()
let attr = prop.GetCustomAttribute<ColumnAttribute>()
where attr != null
select new
{
Prop = prop,
Attrib = attr,
};
For this however, I would probably package it up in a generator. It doesn't need to be written in terms of LINQ, you'd be seriously limiting yourself if you try to.
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);
}
}
Your query becomes this:
var fields = _type.GetProperties()
.SelectWhere(
p => p.GetCustomAttribute<ColumnAttribute>(),
(p, a) => a != null,
(p, a) => new { Prop = p, Attrib = a }
)
.ToList();
Gene
Updated on July 19, 2022Comments
-
Gene almost 2 years
I often find myself writing something like this:
var fields = _type.GetProperties() .Select(prop => new { Prop = prop, Attrib = prop.GetCustomAttribute<ColumnAttribute>() }) .Where(t => t.Attrib != null) .ToList();
Where I'm bothered is that I'm unnecessarily creating objects in the cases where the where clause fails. Granted the overhead is small, but I'd still prefer to save the allocation, as I would if I were simply looping over it or did the more painful:
var fields = _type.GetProperties() .Select(prop => { var attrib = prop.GetCustomAttribute<ColumnAttribute>(); return attrib == null ? null : new {Prop = prop, Attrib = attrib}; }) .Where(t => t != null);
Is there a better pattern/extension method I'm missing? Or is it possible that LINQ could make that optimization under the covers?
Much appreciated!
Update:
I guess something like this is what I mean, but I'm expecting something equivalent already exists and I'm just searching poorly:
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);
-
Mister Epic almost 10 yearsCustom LINQ operators are powerful and unfortunately far too rare. This is a very elegant example.
-
Gene almost 10 yearsWhen you say hide the fact that it's being done, you mean under the covers it allocates for every element, even if attr != null fails?
-
Gene almost 10 yearsEither way, that's definitely more elegant - much appreciate it sir!
-
Jeff Mercado almost 10 years@Gene: Right. That new variable has to be placed somewhere so that all variables can be in scope. Whether it's within a closure or a result object, it has to be done. So you can choose to do it explicitly, or do it implicitly by letting the compiler take care of it.
-
Gene almost 10 years@Jeff: Gotcha, that makes sense. I really should spend more time in the decompiler/sources to fully understand the details. Appreciate your experience again sir!