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);
Select will always return the same number of elements in the list (regardless of a filter condition you may have). Where can return less elements depending on your filter condition.
Select query in LINQ Select method is used to select one or more items from collection or list object, here we see some example of linq select statement. variableName. Select(s => s.Name); There are various ways we can select some records or single record from a collection object.
The Select() method invokes the provided selector delegate on each element of the source IEnumerable<T> sequence, and returns a new result IEnumerable<U> sequence containing the output of each invocation.
In a query expression, the select clause specifies the type of values that will be produced when the query is executed. The result is based on the evaluation of all the previous clauses and on any expressions in the select clause itself. A query expression must terminate with either a select clause or a group clause.
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();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With