I am looking for a way to implement FirstOrEmpty
for a IEnumerable<X>
where X implements IEnumerable<T>
. Basically, if the predicate does not match anything, return Enumerable<T>.Empty
. I do not want to constrain the source parameter to IEnumerable<IEnumerable<X>>
or because I might want to pass in something that implement IEnumerable<X>
(eg IEnumerable<IGrouping<bool, X>>
).
IEnumerable<T> myCollection;
var groupCheck = myCollection.GroupBy(t => t.SomeProp == 23);
var badGroup = groupCheck.FirstOrEmpty(t => !t.Key);
var goodGroup = groupCheck.FirstOrEmpty(t => t.Key);
foreach(T x in badGroup) { ... }
foreach(T x in goodGroup) { ... }
Old way:
IEnumerable<T> myCollection = ...;
var groupCheck = myCollection.GroupBy(t => t.SomePropOnClassT == 23);
var badGroup = (groupCheck.FirstOrDefault(t => !t.Key) ?? Enumerable<T>.Empty);
var goodGroup = (groupCheck.FirstOrDefault(t => t.Key) ?? Enumerable<T>.Empty);
foreach(T x in badGroup) { ... }
foreach(T x in goodGroup) { ... }
public static IEnumerable<TResult> FirstOrEmpty<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate) where TSource : IEnumerable<TResult>
{
TSource tmp = source.FirstOrDefault(predicate);
if(tmp != null) {
foreach(TResult x in tmp)
{
yield return x;
}
}
}
public static IEnumerable<TResult> FirstOrEmpty<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate) where TSource : IEnumerable<TResult>
{
TSource tmp = source.FirstOrDefault(predicate);
return tmp == null ? Enumerable.Empty<TResult>() : tmp;
}
Your own solution:
public static IEnumerable<TResult> FirstOrEmpty<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate
)
where TSource : class, IEnumerable<TResult>
{
return source.FirstOrDefault(predicate) ?? Enumerable.Empty<TResult>();
}
is actually working. I only added the class
constraint. This means " TSource
must be a reference type", and an interface is OK with a class
constraint. The reason for this is that default(TSource)
might not be null
if TSource
were some struct. You can call it like this:
var badGroup = groupCheck.FirstOrEmpty<IGrouping<bool, T>, T>(g => !g.Key);
var goodGroup = groupCheck.FirstOrEmpty<IGrouping<bool, T>, T>(g => g.Key);
Unfortunately, the compiler isn't smart enough to figure out the type parameters itself, so you have to supply them in the angle brackets <..., ...>
like above. I haven't found a solution for that.
Now, if you always use this with IGrouping<,>
, you might want to use the following slightly less general method:
public static IEnumerable<TResult> FirstOrEmpty<TKey, TResult>(
this IEnumerable<IGrouping<TKey, TResult>> source,
Func<IGrouping<TKey, TResult>, bool> predicate
)
{
return source.FirstOrDefault(predicate) ?? Enumerable.Empty<TResult>();
}
This time it goes like this:
var badGroup = groupCheck.FirstOrEmpty<bool, T>(g => !g.Key);
var goodGroup = groupCheck.FirstOrEmpty<bool, T>(g => g.Key);
and the good news is that with this approach the compiler will infer your type arguments, so:
var badGroup = groupCheck.FirstOrEmpty(g => !g.Key);
var goodGroup = groupCheck.FirstOrEmpty(g => g.Key);
works.
I would probably use a refactored version of Attempt 2:
public static IEnumerable<TResult> FirstOrEmpty<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate) where TSource : class, IEnumerable<TResult>
{
return source.FirstOrDefault(predicate) ?? Enumerable.Empty<TResult>();
}
EDIT: I am curious to know why this answer was downvoted.
I came up with the same result as @phoog but am having a hard time getting the compiler to infer the type parameters:
public static IEnumerable<TResult> FirstOrEmpty<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate) where TSource : IEnumerable<TResult>
{
return (IEnumerable<TResult>)source.FirstOrDefault(predicate) ?? Enumerable.Empty<TResult>();
}
Best I have come up with is to explicitly state them:
var badGroup = groupCheck.FirstOrEmpty<IGrouping<bool,int>,int>(t => !t.Key);
var goodGroup = groupCheck.FirstOrEmpty<IGrouping<bool,int>,int>(t => t.Key);
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.