Open shmuelie opened 8 years ago
@jamesqo As I understand, all possible solutions have many issues.
@jamesqo I need first comment from area experts - @OmarTawfik or @VSadov (who is OOF).
I agree with @stephentoub. I think that we should stick to the pattern of other helpers. I don't see it as Try+Verb+Noun
, but as Try+OriginalOperationName
.
If Single were instead GetSingle and SingleOrDefault were instead GetSingleOrDefault, then TryGetSingle would make sense. But they're not. The Try pattern is generally to simply prefix the operation with "Try" and have it return a bool with the actual result as an out parameter, so it seems we should do that here as well. Thus I vote for TrySingle. e.TrySingle is no more strange than e.Single, and there's value in having them be consistent with each other, more so I believe than breaking from the pattern and trying to clarify the name.
@OmarTawfik that has been already "decided". The last open question is -- should we add the Queryable
companions or not?
@karelz that makes sense to me.
Are they direct copy of the Enumerable methods? Can you please update the proposal at the top?
I am only now catching up with this discussion and I would still like to provide my input:
I can second many of @bartdesmet's comments here, and I agree with the reasoning that if we are going to have these methods on IQueryable
the version that returns a Tuple
(or ValueTuple
) seems to be more appropriate that one that uses and output parameter.
But in fact I would also argue that methods that return a Tuple
(or ValueTuple
) are a better fit for LINQ's fluent compositional style in general, even on IEnumerable<T>
.
The bool TryGetX(..., out x)
pattern is and has always been a little awkward to use and kind of the opposite of the style used in LINQ. AFAIR there have been multiple attempts to extend the language to make the former easier/more natural and support for tuple syntax is one of those features.
It might help the whole discussion if we could arrange examples of how the "success" value returned by these operaros can be used when the operators appear nested in a query in both standard method and query comprehension syntax.
@divega
But in fact I would also argue that methods that return a
Tuple
(orValueTuple
) are a better fit for LINQ's fluent compositional style in general, even onIEnumerable<T>
.
I agree. The best thing for these methods to do, actually, would be to return a T?
; it's too bad that Nullable has an artificial struct constraint. Out parameters don't really mix well with other methods because Linq is all about lazy evaluation, even though it works in this case (First
, Single
, etc. are eagerly evaluated).
On the other hand, however, Try
with out is more natural than using a ValueTuple
when working with if statements:
if (e.TryGetFirst(out var first)) { ... }
(bool got, T first) = e.TryGetFirst();
if (got) { ... }
So unless someone has a better idea, it seems like out
is the best choice we have right now.
I would like to use methods with out parameters in public interface, and at the same time, I support to use tuples inside Queryable and providers . I think this is the most convenient solution. The only issue is how find corresponding methods in Enumerable. I see next ways to solve this issue:
public static class QueryableToEnumerableHelper // or some other name
{
public Tuple<bool, T> TrySingle<T>(IEnumerable<T> source)
{
T item;
bool result = Enumerable.TrySingle(source, out item);
return Tuple.Create(result, item);
}
}
Disadvantage: we should search enumerable methods not only in Enumerable class but in QueryableToEnumerableHelper class also.
[EditorBrowsable(EditorBrowsableState.Never)]
attribute. It is like previous solution but helper methods are inside Enumerable class, not inside separated helper class. Disadvantage: user can use such methods.
Have internal methods in Enumerable with tuple for each method with out parameters. It is like previous solution but we need to find internal (not public) methods. Is it possible?
Define rules for Queryable to rewrite internal Tuple methods to Enumerable methods with out parameters. This solution is most complex and have many disadvantages.
I suggest to use second solution as the same universal and convenient. Now I rewrote PR code https://github.com/dotnet/corefx/pull/14276 to see how it will be with second solution.
@divega and @bartdesmet what do you think about such suggestion?
@jamesqo can next
(bool got, T first) = e.TryGetFirst();
if (got) { ... }
be rewritten as next in c# 7.0 or not?
if ((var tryFirst = e.TryGetFirst()).Item1) { tryFirst.Item2 }
But in any way it is bad code style.
@divega @bartdesmet @karelz I wrote some my design suggestions in https://github.com/dotnet/corefx/issues/6073#issuecomment-266939214
@AlexRadch I can't remember any precedent of LINQ operators that were designed to only be used at the top of a query. That is why I am still trying to understand if these operators could be used inside a query and how the usage would look like.
I also still believe there is a lot of value in having symmetry between the Enumerable
and Queryable
stories.
All in all if we cannot come up with something that works everywhere in the query and for both Enumerable
and Queryable
I would be inclined not to make these operators part of our core libraries. Applications are free to define them if they really need them (i.e. for those not so frequent cases in which the default value of TSource
is not enough to indicate failure to retrieve a value).
A simple implementation that I would hesitate to productize, but that would probably work ok in many cases for existing IQueryable<TSource>
providers looks reasonable and simple enough:
public static bool TryFirst<TSource>(
IQueryable<TSource> source,
Expression<Func<TSource, bool>> predicate,
out TSource first)
{
foreach(var element in source.Where(predicate)) // consider adding .Take(1)
{
first = element;
return true;
}
first = default(TSource);
return false;
}
Implementing a similar version of TrySingle()
reminded me about Single()
's two failure modes (I see it already came up earlier in this thread) which make this one feels a bit weirder:
public static bool TrySingle<TSource>(
this IQueryable<TSource> source,
Expression<Func<TSource, bool>> predicate,
out TSource single)
{
var firstFound = false;
single= default(TSource);
foreach (var element in source.Where(predicate)) // consider adding .Take(2)
{
if (firstFound)
{
return false;
}
else
{
single = element;
firstFound = true;
}
}
return firstFound;
}
@divega Thanks for great answer! As I see you suggest to add implementation inside Queryable and call old API for drivers. It makes sense because Try methods are not deferred.
I will made such implementation in this PR.
As I see you suggest to add implementation inside Queryable and call old API for drivers.
@AlexRadch Not at all! I did not intend to suggest that, sorry if I wasn't clear. I am saying that this is the kind of implementation application developers could include in their own applications if it really helps them save a few lines of code, and I am questioning the value of including any implementation these methods in the .NET BCL, for either IEnumerable<T>
or IQueryable<T>
@divega
(i.e. for those not so frequent cases in which the default value of TSource is not enough to indicate failure to retrieve a value)
I don't think it's so infrequent. Consider any generic method that wants to get the first value of an enumerable-- there's no way to do it smoothly and performantly with the current APIs. IMO, I don't see a good reason to block this proposal.
I do have an idea, however. Perhaps this is a bit radical, but what if we introduced an Optional<T>
type to System.Linq
that we could return from these functions? It would implement the Maybe monad and could be used in Linq query comprehensions. It would be similar to Nullable
, except without the artificial struct constraint.
namespace System.Linq
{
public struct Optional<T>
{
public Optional(T value);
public static Optional<T> Empty { get; }
public bool HasValue { get; }
public T Value { get; }
public T GetValueOrDefault();
public bool TryGetValue(out T value);
public Optional<TResult> Select<TResult>(Func<T, TResult> selector);
public Optional<TResult> SelectMany<TMiddle, TResult>(Func<T, Optional<TMiddle>> selector1, Func<T, TMiddle, Optional<TResult>> selector2);
}
}
We could then return this from the TryGet*
methods: (edit: This was meant to be Try*
. Corrected.)
public static Optional<T> TryFirst<T>(this IEnumerable<T> source);
public static Optional<T> TryLast<T>(this IEnumerable<T> source);
public static Optional<T> TrySingle<T>(this IEnumerable<T> source);
// + the predicate overloads
and use it like this
Optional<string> GetFirstString<T>(IEnumerable<object> objects)
{
// If `objects` is null, the selector is not run and Optional<string>.Empty is returned
return objects.TryFirst().Select(o => o.ToString());
}
// People who like the `out` pattern can still use it
if (ints.TryFirst().TryGetValue(out i))
{
Console.WriteLine(i);
}
else
{
Console.WriteLine("No ints found.");
}
// Compatible with query comprehensions
class Person { Optional<Person> Parent; }
Person person = ...
var greatGrandparent = from parent in person.Parent
from grandparent in parent.Parent
select grandparent.Parent;
I don't think it's so infrequent.
@jamesqo My statements were intended to be subjective :smile: I don't have the means to accurately measure how often it is necessary to discriminate between a default value and a non-existent value. I just made an educated guess that needing this is less frequent than not needing it, and I am also making the assumption that people can figure out other patterns when they actually do need it. Those patterns may be a bit more complicated than the existing methods in LINQ but they look reasonable to me, especially assuming that they are only necessary in a less common case. I also happen to not be a fan of the TryGetX(..., out x)
pattern. I know it sometimes can have advantages (especially with inline var support) but I still prefer other alternatives when available.
I do have an idea, however. Perhaps this is a bit radical, but what if we introduced an Optional
type to System.Linq that we could return from these functions?
I actually think this is a very interesting idea. In fact while going through some of the proposed designs on the whiteboard a few days ago I also thought that tuples weren't nice enough and that introducing a type that had more specific semantics would probably make sense. I was actually surprised that @bartdesmet had not mentioned this possibility already, or did he? :smile:
But then I realized that an IQueryable<T>
or IEnumerable<T>
can convey a very similar concept and so I rejected (perhaps too quickly?) the idea of introducing a new type. E.g., the result of
source.Where(predicate).Take(1);
already conveys mostly the same concept as an Optional<T>
, with the possible advantage that it stays in the same monad that you started on, e.g. if source
happens to be an IQueryable<T>
the result would also be an IQueryable<T>
and conversely, if source
is an IEnumerable<T>
the result would be an IEnumerable<T>
.
However I understand that a type like the one you are proposing (or perhaps the equivalent interface) would carry more precise semantics. E.g. an instance of Optional<T>
could never represent a collection of multiple elements. I also suspect the type you propose could find other applications in the future, e.g.: to represent optional services on https://github.com/aspnet/DependencyInjection/, a new general API pattern that replaces TryGetX(..., out x)
, and perhaps even as part of the [non]nullable reference types story?
Initially I was a bit worried about the possibility of a proliferation of types, but I am not sure these are real issues:
Optional<T>
or would the IQueryable<T>
version of the methods just return the same types as the IEnumerable<T>
versions? Optional<T>
or will [Value]Task<Optional<T>>
and Optional<[Value]Task<T>>
be enough?Also, this particular code snippet:
if (ints.TryGetFirst().TryGetValue(out i))
{
Console.WriteLine(i);
}
made me think that perhaps we should consider coming up with a new naming pattern for methods that return Optional<T>
rather than overloading the existing TryGet...
naming. E.g.:
FirstAsOptional()
OptionalFirst()
Although I am not convinced of this either (I don't like any of the names I could come up with).
I would love to hear what others think about your idea.
@divega I was waiting for others to reply in the meantime, but since no one has:
I also suspect the type you propose could find other applications in the future,
I think so too; places like Dictionary.TryGetValue
would be ideal candidates.
Would we need a separate IQueryable flavor o Optional
or would the IQueryable version of the methods just return the same types as the IEnumerable versions? Would we eventually need an async version of Optional
or will [Value]Task<Optional > and Optional<[Value]Task > be enough?
I don't think there is any need to add more "flavors"-- the type should work fine with existing containers.
I would love to hear what others think about your idea.
👍 I would also like to hear what others think.
Also: If we were to pursue this, a nicer (and shorter) name would be Maybe<T>
.
cc @bartdesmet and also @MadsTorgersen and @mattwar for their perspective from the original LINQ design philosophy (there have been a few recent discussions about new LINQ operators that could use your insights, I will try to tag you more often).
Let me express an opinion here, as someone who was around for at least part of the original LINQ design. @mattwar was more deeply involved in shaping IQueryable
and may have additional and/or conflicting perspective. :-)
There are probably 10x (unscientific guesstimate) more people using LINQ over IEnumerable
than IQueryable
. We should land on the API shape that is most convenient/elegant/idiomatic for that larger crowd, and solve any IQueryable
-specific problems within the scope of IQueryable
. Degrading the usage experience for everyone isn't the right outcome.
For that reason, TrySingle
etc. should follow the established Try
pattern, with an out parameter and a bool
return value. All the good arguments to that effect above apply.
What to do about IQueryable
? The problem there is that the current IQueryable
machinery isn't able to deliver an expression tree of such a call where part of the result comes through an out parameter. Hence proposals in dotnet/corefx#14276 to instead return a tuple or other compound result.
I think IQueryable
should have such tuple-returning overloads of TrySingle
etc. Those do indeed construct expression trees of calling themselves, like all other Queryable
methods do.
public static (bool success, T value) TrySingle<T>(this IQueryable<T> source) { ... }
In addition there should be an overload that shadows the out-based one on IEnumerable
, and simply forwards to the tuple-based one:
public static bool TrySingle<T>(this IQueryable<T> source, out T value)
{
bool success;
(success, value) = TrySingle(source);
return success;
}
This limits the damage only to the Queryable
arena, rather than inflict it on Enumerable
users.
The "damage" is:
Queryable
has two public TrySingle
methods instead of one, which may confuse users and query providers alike. The providers will still mostly see the tuple-based one, but may still see the out-based one when nested in other queries. They'll just have to be smart enough to recognize both. For the users, we could hide the tuple-based (less convenient) methods from IntelliSense.Queryable
methods - the out-based overloads- that do not completely follow the otherwise ubiquitous pattern of encoding themselves into an expression tree for providers to see. But it's close enough that I don't think we're deviating morally from that principle: they are encoding another, equivalent, overload of themselves.Thoughts?
@MadsTorgersen Is the out variable version of TrySingle known to not work with IQueryable?
@mattwar I'm relying on @bartdesmet's argument to that effect over in dotnet/corefx#14276.
Thanks @MadsTorgersen. From the perspective of optimizing for the most common usage and the hypothesis that IEnumerable<T>
is the most common scenario, I am fine with us doing what you are proposing (which is I believe what people in the thread had already arrived to before I started chiming in :grin:).
I still believe these operators are weird in that they have even more limited composability than the existing operators that return a single element (the argument I tried to make at https://github.com/dotnet/corefx/issues/6073#issuecomment-268405835), but I can be convinced that this is not as important.
@MadsTorgersen :shipit: Sounds like a great idea to me. I think it makes sense to stick with existing conventions (using out
) and not fall too far from the tree. Tuples do have limited readability compared to Try
in this kind of scenario.
With the addition of out var it makes TryXXX pattern API's less unappealing for general code (basically because you don't have to define the variable up front.) Yet, this is still an unappealing pattern for a LINQ type API that is not only primarily expression based, but is sequence based and is likely to have nested expressions that are executed for each item in that sequence that may themselves be needing access to this API. If you only have a TryXXX pattern then this becomes a bad choice if the only way to use it requires you to use the same variable, associated with at best the outer statement scope, for each instance of the iteration. Your query now becomes side-effecting and cannot be processed in parallel.
An API that returns Option<T>, some tuple pattern, or other like structure is better for this sequence style usage, except the language does not yet offer good means to consume these compound values in an expression context.
We did consider this during original LINQ design. This is why we ended up with SingleOrDefault style API. While not perfect, it doesn't cause side-effects while nested and doesn't require new language patterns to consume.
@mattwar Are you talking about something like the following?
from x in collection
where x.AnotherCollection.TrySingle(out var y)
select y
There is a proposal to make that work: https://github.com/dotnet/roslyn/issues/15619.
@svick that would work, but only for query expression syntax. Its an awkward translation if you had to do it by hand. Assuming the translation would work at all.
The PR dotnet/corefx#14276 seems to be abandoned. Unassigning this issue - it is "up for grabs" again, available for anyone to pick it up.
It looks like we got consensus on the design of IQueryable
companion APIs. Can someone please produce the full API shape for IQueryable
? -- Basically list of ALL IQueryable
APIs (incl. which type they will live in - Queryable
?) based on what @MadsTorgersen suggested above:
public static (bool success, T value) TrySingle<T>(this IQueryable<T> source);
public static bool TrySingle<T>(this IQueryable<T> source, out T value);
Once we have it, we can update top-most spec and make it available for someone to implement it.
@karelz One thing to consider, btw, is whether we should use camelcase or pascal case for the tuple's element names, e.g. (bool success, T value)
or (bool Success, T Value)
. I'm not sure if there's an established convention for that yet.
Tuples have argument list semantics rather than structure semantics. I think the more that logic about tuple values is distributive to each individual value, the better. If you wouldn't pascal-case arguments or multiple return values (if we had those), you probably shouldn't pascal-case tuple element names.
Is there anything I can to to help move this forward?
See my last 2 statements above: https://github.com/dotnet/corefx/issues/6073#issuecomment-273845281 and https://github.com/dotnet/corefx/issues/6073#issuecomment-273844248
@karelz Too late for 2.0?
I think so -- see https://github.com/dotnet/corefx/issues/17619#issuecomment-296872132 for details. It is about minimizing risk. Rushing APIs is never a smart idea. Small mistake can cost you life-time of compat problems. It's just not worth the risk, unless the value is really HUGE. Makes sense?
Yup, I have no problem with that. Just making sure my time is put to good use!
Yep, appreciate it. For best use of time, I'd suggest to check: dotnet/corefx#17619 (top post).
I was happy to read @MadsTorgersen's comment but I have to say, @MadsTorgersen, that I am kind of taken aback by the 'IQueryable
can eat the table scraps' sentiment.
I have to say this is not looking good... there is already a good way to achieve the desired result without muddying up the LINQ API, and the amount of deliberation that has gone back and forth on this issue and its associated PR(s) highlights the shakiness of it.
If the main motivation is the inability to distinguish between a default value in correlation to the count of elements within a sequence, why not use more conventional means? I understand that IEnumerable
is by far the majority use case for LINQ, but I don't think I could see the methods introduced by this proposal being so commonly used as to have enough benefit over some conventional equivalents that accomplish the same end result to warrant introducing so many unprecedented constructs to the LINQ API, like out
parameters, tuple returns, and 'matched-mismatched' operators between Enumerable
and Queryable
:
// Top-level equivalent
var trySingle = xs.Where(x => x.y).Take(2);
var tryFirst = xs.Where(x => x.y).Take(1);
var tryLast = xs.Where(x => x.y).Reverse().Take(1);
var tryElementAt = xs.Where(x => x.y).Skip(index).Take(1);
var anyOneOfThoseExamples = PickOne(trySingle, tryFirst, tryLast, tryElementAt);
var result = anyOneOfThoseExamples.ToArray();
if (result.Length == 1)
{
// Do stuff
}
else
{
// Fall back
}
// Subquery equivalent
var sequence = from x in xs
let y = ys.Where(y => y.x == x).Take(2).ToArray()
select new
{
x,
y = (success: y.Length == 1, value: y[0] )
};
Just my :two: 💰 ...
why not use more conventional means?
LINQ is by definition NOT conventional, since in any case where LINQ is used you can do it using for
/foreach
and collections. LINQ generally (in my opinion at least) provides developers with:
While yes TrySingle
, TryFirst
, TryLast
, and TryElementAt
, can done using existing operators they are less obvious, less performant (have a huge collection and now you have to allocate an array to just find out that it has more than one item?), and do read like a sentence.
Is the LINQ API getting unmanageable? Maybe. But that should be solved by splitting up the methods, not by limiting its usefulness.
LINQ is by definition NOT conventional, since in any case where LINQ is used you can do it using
for/foreach
and collections.
I didn't mean "conventional" as in "let's take it down to the assembly registers", I meant it as in "what LINQ already offers to accomplish this."
less performant (have a huge collection and now you have to allocate an array to just find out that it has more than one item?)
Without doing a thorough analysis, I would assume that somewhere along the line, TrySingle
and kin would need to allocate at least one or two objects, if not a struct under the guise of an IEnumerator
or something like that. Allocating an array is nothing to worry about without profiling; especially in the examples I provided, the array would hold at most one or two elements.
Is the LINQ API getting unmanageable? Maybe. But that should be solved by splitting up the methods, not by limiting its usefulness.
I don't understand what you mean here. I don't think the LINQ API is unmanageable... yet. I think this proposal would be a big step in that direction, though. When you say "splitting up the methods", I think about how easily TrySingle
is achieved by composing Take
and ToArray
. When you say "limiting its usefulness", I think about how TrySingle
doesn't really add any new usefulness -- it adds a sugar over something that was already possible, only this sugar might make Queryable
sick and throw up on itself. 🤢
Without doing a thorough analysis, I would assume that somewhere along the line,
TrySingle
and kin would need to allocate at least one or two objects, if not a struct under the guise of anIEnumerator
or something like that. Allocating an array is nothing to worry about without profiling; especially in the examples I provided, the array would hold at most one or two elements.
I was incorrect there. Yes, in your examples the array would hold one or two items at most. And yes optimization without profiling is never a good idea.
don't understand what you mean here. I don't think the LINQ API is unmanageable... yet. I think this proposal would be a big step in that direction, though. When you say "splitting up the methods", I think about how easily
TrySingle
is achieved by composingTake
andToArray
. When you say "limiting its usefulness", I think about howTrySingle
doesn't really add any new usefulness -- it adds a sugar over something that was already possible, only this sugar might makeQueryable
sick and throw up on itself. 🤢
I think the key word here is "sugar". Yes, TrySingle
and kin are syntactic sugar, but so is all of LINQ, and even plenty of C# and VB.NET. The question (and we're going to have to agree to disagree here) is if the cost of the sugar is worth it. I think it is, and clearly you don't.
I don't know how to judge implementations, but I would have done this rather than foreach
with a flag:
public static bool TrySingle<T>(this IEnumerable<T> source, out T value)
{
if (source == null) throw new ArgumentNullException(nameof(source));
using (var en = source.GetEnumerator())
{
if (en.MoveNext())
{
var temp = en.Current;
if (!en.MoveNext())
{
value = temp;
return true;
}
}
value = default;
return false;
}
}
Just curious because stuff like this is sitting in my Extensions.cs and I may as well use the official implementation if I can deduce from the discussion what that would be.
@jnm2 well Single
is done similarly to yours because it beat foreach
with a flag when compared a while back. Also I think it's more intuitive; look for the thing you want, and then look to see if there's another.
@jnm2 I skimmed through the thread -- looks like we are still waiting on https://github.com/dotnet/corefx/issues/6073#issuecomment-273845281. Nobody picked it up yet:
Can someone please produce the full API shape for
IQueryable
? -- Basically list of ALLIQueryable
APIs (incl. which type they will live in -Queryable
?) based on what @MadsTorgersen suggested above:public static (bool success, T value) TrySingle<T>(this IQueryable<T> source); public static bool TrySingle<T>(this IQueryable<T> source, out T value);
Once we have it, we can update top-most spec and make it available for someone to implement it.
Here's what I care about, plus the IQueryable versions. Are we doing predicate overloads?
namespace System.Linq
{
public static class Enumerable
{
+ public static bool TrySingle<TSource>(this IEnumerable<TSource> source, out TSource element);
}
public static class Queryable
{
+ public static (bool success, T value) TrySingle<T>(this IQueryable<T> source);
+ public static bool TrySingle<T>(this IQueryable<T> source, out T value);
}
}
@jnm2 we need also the other APIs - someone should reread the thread discussion and find out if we planned to do all.
I reread the whole thread twice just now and don't see much mention of either the TryFirst, TryLast and TryElementAt variants or the predicate variants. Certainly not in conjunction with Queryable
. I'm not sure why it's me doing this, but here's everything, just to get the ball rolling:
namespace System.Linq
{
public static class Enumerable
{
public static TSource Single<TSource>(this IEnumerable<TSource> source);
public static TSource Single<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
public static TSource SingleOrDefault<TSource>(this IEnumerable<TSource> source);
public static TSource SingleOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
+ public static bool TrySingle<TSource>(this IEnumerable<TSource> source, out TSource element);
+ public static bool TrySingle<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, out TSource element);
public static TSource First<TSource>(this IEnumerable<TSource> source);
public static TSource First<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source);
public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
+ public static bool TryFirst<TSource>(this IEnumerable<TSource> source, out TSource element);
+ public static bool TryFirst<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, out TSource element);
public static TSource Last<TSource>(this IEnumerable<TSource> source);
public static TSource Last<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
public static TSource LastOrDefault<TSource>(this IEnumerable<TSource> source);
public static TSource LastOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
+ public static bool TryLast<TSource>(this IEnumerable<TSource> source, out TSource element);
+ public static bool TryLast<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, out TSource element);
public static TSource ElementAt<TSource>(this IEnumerable<TSource> source, int index);
public static TSource ElementAtOrDefault<TSource>(this IEnumerable<TSource> source, int index);
+ public static bool TryElementAt<TSource>(this IEnumerable<TSource> source, int index, out TSource element);
}
public static class Queryable
{
public static TSource Single<TSource>(this IQueryable<TSource> source);
public static TSource Single<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
public static TSource SingleOrDefault<TSource>(this IQueryable<TSource> source);
public static TSource SingleOrDefault<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
+ public static (bool success, T value) TrySingle<T>(this IQueryable<T> source);
+ public static (bool success, T value) TrySingle<T>(this IQueryable<T> source, Expression<Func<TSource, bool>> predicate);
+ public static bool TrySingle<TSource>(this IQueryable<TSource> source, out TSource element);
+ public static bool TrySingle<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate, out TSource element);
public static TSource First<TSource>(this IQueryable<TSource> source);
public static TSource First<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
public static TSource FirstOrDefault<TSource>(this IQueryable<TSource> source);
public static TSource FirstOrDefault<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
+ public static (bool success, T value) TryFirst<T>(this IQueryable<T> source);
+ public static (bool success, T value) TryFirst<T>(this IQueryable<T> source, Expression<Func<TSource, bool>> predicate);
+ public static bool TryFirst<TSource>(this IQueryable<TSource> source, out TSource element);
+ public static bool TryFirst<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate, out TSource element);
public static TSource Last<TSource>(this IQueryable<TSource> source);
public static TSource Last<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
public static TSource LastOrDefault<TSource>(this IQueryable<TSource> source);
public static TSource LastOrDefault<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
+ public static (bool success, T value) TryLast<T>(this IQueryable<T> source);
+ public static (bool success, T value) TryLast<T>(this IQueryable<T> source, Expression<Func<TSource, bool>> predicate);
+ public static bool TryLast<TSource>(this IQueryable<TSource> source, out TSource element);
+ public static bool TryLast<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate, out TSource element);
public static TSource ElementAt<TSource>(this IQueryable<TSource> source, int index);
public static TSource ElementAtOrDefault<TSource>(this IQueryable<TSource> source, int index);
+ public static (bool success, T value) TryElementAt<T>(this IQueryable<T> source, int index);
+ public static bool TryElementAt<TSource>(this IQueryable<TSource> source, int index, out TSource element);
}
}
@jnm2 I'm not sure why it's me doing this
You were interested in the issue and no one else was interested to follow up in the last year, which means it won't happen otherwise. I am just trying to help here to push it forward and unblock contributors (you) :) ... maybe I misunderstood your intent?
LGTM, adding it to the top post (it is considered approved - I will ping API reviewers as FYI). Thanks @jnm2! FYI: @OmarTawfik @VSadov
Next steps: it is available for implementation if someone wants to grab it (not blocked anymore).
I'm interested but I must be the least knowledgeable person in this thread... I guess I won't let that stop me in the future. 😂
I'm also interested enough to implement it if no one else does...
Motivation
In LINQ we have the
Single
andSingleOrDefault
methods, but they don't tell you if it succeeded or not.The problem with these methods is that if there is more than one element in
source
(or more than one matching element in case ofpredicate
) they all throw aSystem.InvalidOperationException
. That is fine if more than one element is an error or very rare. If more than one element is common or expected then this will cause major slow downs.Additionaly, in cases of (example: int collections), default(int) is 0, which wouldn't tell the caller whether the operation succeeded (returning the item 0) or not (returning the default 0).
Proposal
Original
I'd like to propose a new LINQ method:
TrySingle
. The idea is pretty much the same as the existingSingle
method except it does not throw an exception when there is not only a single item in the sequence.To do this I made to modifications to the
Single
contract:bool
.true
if the sequence contains exactly one item,false
otherwise.TSource
. This will "hold" the single element on success. On failure it is filled withdefault(TSource)
.I have found this method very useful in my own code and if accepted can quickly put together a pull request.