Repository navigation
Allow for specify return value on System.Linq.Enumerable.*OrDefault methods #20064
Copy link
Copy link
Closed
Closed
Copy link
Labels
api-approvedAPI was approved in API review, it can be implementedAPI was approved in API review, it can be implementedarea-System.Linqhelp wanted[up-for-grabs] Good issue for external contributors[up-for-grabs] Good issue for external contributors
Milestone
Description
I would like to propose adding overloads to the System.Linq.Enumerable.*OrDefault and System.Linq.Queryable.*OrDefault methods for specifying the value to be returned in the default case. This is the value that would be returned instead of default(TSource) when there are no items in the enumeration
There are times when the default value of a given type may be a valid value from the enumeration. If the default value for the enumeration type is valid value, there is not a nice way to determine if the returned values was because the enumeration was empty or if that value was in the enumeration.
Proposed API change
namespace System.Linq
{
public static class Enumerable
{
public static TSource SingleOrDefault<TSource>(this IEnumerable<TSource> source, TSource defaultValue);
public static TSource SingleOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, TSource defaultValue);
public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source, TSource defaultValue);
public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, TSource defaultValue);
public static TSource LastOrDefault<TSource>(this IEnumerable<TSource> source, TSource defaultValue);
public static TSource LastOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, TSource defaultValue);
}
public static class Queryable
{
public static TSource SingleOrDefault<TSource>(this IQueryable<TSource> source, TSource defaultValue);
public static TSource SingleOrDefault<TSource>(this IQueryable<TSource> source, Func<TSource, bool> predicate, TSource defaultValue);
public static TSource FirstOrDefault<TSource>(this IQueryable<TSource> source, TSource defaultValue);
public static TSource FirstOrDefault<TSource>(this IQueryable<TSource> source, Func<TSource, bool> predicate, TSource defaultValue);
public static TSource LastOrDefault<TSource>(this IQueryable<TSource> source, TSource defaultValue);
public static TSource LastOrDefault<TSource>(this IQueryable<TSource> source, Func<TSource, bool> predicate, TSource defaultValue);
}
}Updates
- Switch from using default parameters to method overloads
- Added IQueryable methods as well
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
api-approvedAPI was approved in API review, it can be implementedAPI was approved in API review, it can be implementedarea-System.Linqhelp wanted[up-for-grabs] Good issue for external contributors[up-for-grabs] Good issue for external contributors
Activity
jnm2 commentedon Jan 31, 2017
This would be an alternative to having to switch from
SingleOrDefault(predicate)toDefaultIfEmpty(x).Single(predicate).karelz commentedon Feb 1, 2017
You are adding new methods (we can't change existing ones).
I think the
= default(TSource)will cause compiler ambiguity with the existing methods - we need to drop the default value IMO (please check first if I am not mistaken).JonHanna commentedon Feb 1, 2017
It wouldn't cause ambiguity at the compiler level, but the default would never be used because the existing methods would always beat it in the overload resolution, so it's pointless. It could certainly cause ambiguity at the human understanding level.
If we had this (I've no strong opinion either way on that) we should also have
Queryableequivalents.jnm2 commentedon Feb 1, 2017
I believe the way these conversations usually go is that you can't use optional parameters without breaking binary back compat. http://haacked.com/archive/2010/08/10/versioning-issues-with-optional-arguments.aspx
JonHanna commentedon Feb 1, 2017
I think it would be safe in that regard, given the types involved, (though it could break someone reflecting and using the count of parameters to find which overload they wanted), but I wouldn't bet on that, and it could still mess with some future change, and in any case they're still pointless defaults as they'll never be used.
Keboo commentedon Feb 1, 2017
@karelz and @JonHanna I updated the issue description based on your comments (I think I did it correctly; first time filing an issue in this repo).
karelz commentedon Feb 1, 2017
@Keboo all good so far, we will let you know if you're not following "the rules" ;-)
FYI: Next steps in API review process are:
Don't expect super-fast turnaround. New APIs need bake time and some thinking through. The process should not be rushed even for obvious things.
If you are eager to contribute to CoreFX, I would recommend to grab some 'up for grabs' issues (which are not API proposals) in the meantime - the most valuable/impactful things are:
karelz commentedon Feb 1, 2017
@jnm2 AFAIK the proposal only adds APIs, it of course can't remove any ;-)
jnm2 commentedon Feb 1, 2017
@karelz Ah, misunderstood the purpose of the original proposal. Latest revision makes sense.
jamesqo commentedon Feb 1, 2017
Would it be better to have a selector run for the default value instead of a concrete value?
I wonder if we could also pass in more information to the
SingleOrDefaultselector, like whether the enumerable was empty or had more than 1 element.Keboo commentedon Feb 2, 2017
@jamesqo I am not sure the selector adds much value here. Can you elaborate on when you would find it useful?
As for the SingleOrDefault case, it sounds like you are expecting this selector to get invoked in both when the enumeration is empty and when it contains multiple items. Wouldn't this be a change in the behavior of SingleOrDefault?
MarkMichaelis commentedon Feb 2, 2017
This makes sense to me... good suggestion @Keboo.
jamesqo commentedon Feb 5, 2017
@Keboo Sorry for the late reply.
I was thinking it could be used to lazily load a value if there was no item matching the predicate. Looking back though, I agree it may not make much sense, mainly because Linq is used with pure functions so a argument-less function would go against the grain.
@karelz Everyone seems to think this is a good idea, so maybe it can be marked ready-for-review. (I have another idea for these methods which I plan to open a proposal for later, but I won't block this on it.)
karelz commentedon Feb 5, 2017
@jamesqo area experts are supposed to make the first-level API approval, by marking the API as such: @VSadov @OmarTawfik can you please check if the API proposal is in line with Linq direction?
OmarTawfik commentedon Feb 7, 2017
LGTM. @VSadov?
I think it is useful addition to the API.
26 remaining items
eiriktsarpalis commentedon Nov 13, 2020
Thanks for volunteering @Foxtrek64, I have assigned the issue to you.
Joe4evr commentedon Nov 13, 2020
@Foxtrek64 For reference, a similar method that has existed for ages in .NET is
Nullable<T>.GetValueOrDefault(T defaultValue).GeraudFabien commentedon Nov 13, 2020
You could use the operator "??" wich is great because :
For any case where you have a struc like dictionary, int, datetime i use two trick depending of the case
I used to implement this overload for one reason. When you read "or default" i expect my first param to be default. With "??" the default is after the predicate wich can be long in some case and become hard to read.
TheBrambleShark commentedon Dec 7, 2020
A few questions-
System.Collections.Immutablehas its own LINQ library. Should I extend this, or any additional libraries, in this change?Should we add
ElementAtOrDefault<TSource>(this IEnumerable<TSource> source, int index, TSource other);to this change? It was not included in the original scope but I feel it is likely a logical addition.KieranDevvs commentedon Jan 25, 2021
That wouldn't work, here's why:
Take this example:
In this scenario, you would have to change the collection data type to allow nullable values, and in that case, you don't know if null is a valid value from the query, or if its not found anything.
For example:
If the source first item is null,
FirstOrDefaultreturns the item (null) and then the coalesce returns 5.If the source is empty,
FirstOrDefaultreturnsdefault(T)ofint?which isnull, and the coalesce returns 5.In other words, this isn't an alternative solution to the problem because there is no way to determine between default(T) and a valid value.
eiriktsarpalis commentedon Jan 26, 2021
Hi @Foxtrek64, per your question you should just stick to the approved APIs for now. Consider creating a new issue if you feel that it could also be added to immutable collections.
Fix dotnet#20064
Allow for specify return value on System.Linq.Enumerable.*OrDefault m…