Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FirstOrNullable and similar method for struct collection #28650

Open
Thaina opened this issue Feb 8, 2019 · 17 comments
Open

FirstOrNullable and similar method for struct collection #28650

Thaina opened this issue Feb 8, 2019 · 17 comments
Labels
api-needs-work API needs work before it is approved, it is NOT ready for implementation area-System.Linq wishlist Issue we would like to prioritize, but we can't commit we will get to it yet
Milestone

Comments

@Thaina
Copy link

Thaina commented Feb 8, 2019

Please add these to System.Linq.Enumerable

public static T? FirstOrNull<T>(this IEnumerable<T> items) where T : struct {}
public static T? LastOrNull<T>(this IEnumerable<T> items) where T : struct {}
public static T? ElementAtOrNull<T>(this IEnumerable<T> items,int index) where T : struct {}

// and so on
@Grauenwolf
Copy link

I assume that this would be equivalent to var x = list.Cast<int?>().FirstOrDefault();

I think that FirstOrNull is a better name.

@Thaina
Copy link
Author

Thaina commented Feb 8, 2019

@Grauenwolf Not sure but I think implementation is even easier than that

public static T? FirstOrNullable<T>(this IEnumerable<T> items) where T : struct
{
    var iter = items.GetEnumerator();
    if(!iter.MoveNext())
         return (T?)null;

    return iter.Current;
}

I don't have naming preference just only want this functionality

@jnm2
Copy link
Contributor

jnm2 commented Feb 8, 2019

FirstOrNull makes much more sense. 'Nullable' includes both null and values.

I have *OrNull extension methods that travel around with me for pretty much every *OrDefault BCL API, e.g. GetValueOrNull(TKey key) on IReadOnlyDictionary.

@bugproof
Copy link

bugproof commented Mar 21, 2019

@Grauenwolf no it would be faster, Cast<T?> is slow. I vote for FirstOrNull, LastOrNull etc.

@Clockwork-Muse
Copy link
Contributor

@dark2201 - I'm pretty sure the intent was that the behavior was the same.

Although it's true that adding an extra operator adds processing time, I'm not sure that a First case is going to really make a difference. Last, though, will need to convert everything before it can return any result.

@msftgits msftgits transferred this issue from dotnet/corefx Feb 1, 2020
@msftgits msftgits added this to the Future milestone Feb 1, 2020
@maryamariyan maryamariyan added the untriaged New issue has not been triaged by the area owner label Feb 23, 2020
@eidylon
Copy link

eidylon commented Aug 12, 2020

While this would be awesome, I do think it should be broader than just structs. It is something that would be helpful for all value types. ints, bools, or even enums. 100% vote for this, but for all value types.

@Grauenwolf
Copy link

An int or boolean is a struct in .NET. You're probably thinking of C.

@eidylon
Copy link

eidylon commented Aug 14, 2020

Huh. Did not realize that. Yup... learn something new every day. Okay then, I guess my comment is extraneous. 👍🏼👍🏼

@adamsitnik adamsitnik removed the untriaged New issue has not been triaged by the area owner label Sep 2, 2020
@eiriktsarpalis
Copy link
Member

Would you be able to provide an example of how such a method would be used? Presumably the motivation is to work around the shortcomings of GetValueOrDefault() when used over value types? If that is the case, would the already approved overloads in #20064 not suffice to work around this problem?

@eidylon
Copy link

eidylon commented Nov 23, 2020

From giving it a quick look (didn't read fully in-depth), I think so. The problem I came here and added my vote for was the use case mentioned in that thread... of needing to return a default value from an Enumberable of enums, but not being able to use FirstOrDefault, because "was it a valid value (0) or was it empty, and so it returned default (0)."

@jnm2
Copy link
Contributor

jnm2 commented Nov 23, 2020

@eiriktsarpalis The OrDefault methods approved in #20064 can't cover this use case because they do not allow lazy evaluation to obtain the default value.

With FirstOrNull:

var foo =
    list.FirstOrNull(f => f.IsPrimary)
    ?? list.FirstOrNull()
    ?? SomeExpensiveOperation();

How would you implement the same thing with the proposal in #20064? Even if the OrDefault methods gained Func<T> defaultFactory methods, the syntax would involve unwieldly nesting (and avoidable allocations) rather than a linear pipeline.

Taking a look at how the Roslyn compiler codebase uses these extension methods might be useful.

@Thaina
Copy link
Author

Thaina commented Nov 23, 2020

@eiriktsarpalis

var intArray = new[] { ... };
var firstLessThanOne = intArray.FirstOrNull((i) => i < 1);

With FirstOrDefault, you can't distinguish between there is zero existing in the list, or there is no number less than one
With 20064, you don't have ability to specify null as default for struct, or you need to cast number to be nullable

@eiriktsarpalis
Copy link
Member

@jnm2 Noted. With the upcoming inclusion of discriminated unions in future versions of C#, I wouldn't be too surprised if we ended up adding a true option type. This would provide a solution that works for all types and not just structs.

@jnm2
Copy link
Contributor

jnm2 commented Nov 23, 2020

As long as the syntax example above wouldn't become belabored, I like that concept a lot. The solution for non-nullable reference types in that example is of course to swap FirstOrNull with FirstOrDefault, but there is no nice solution for nullable reference types without an option type.

@eiriktsarpalis eiriktsarpalis added the api-needs-work API needs work before it is approved, it is NOT ready for implementation label Jan 20, 2021
@terrajobst terrajobst removed the api-suggestion Early API idea and discussion, it is NOT ready for implementation label Jun 25, 2021
@eiriktsarpalis eiriktsarpalis added the wishlist Issue we would like to prioritize, but we can't commit we will get to it yet label Nov 1, 2021
@andre-ss6
Copy link

I just stumbled upon a use case for this:

Current alternatives:

// besides having to cast everything to Nullable, I have to use ! and .Value inside the predicate, unnecessarily... :(
var uf = addressComponents.EnumerateArray().Cast<JsonElement?>()
    .FirstOrDefault(a => a!.Value.GetProperty("types").EnumerateArray().Select(t => t.GetString())
                                    .Contains(address_type_1))?
    .GetProperty("short_name").GetString();

// OR

// without the cast to JsonElement?, I'm unable to use the safe navigation operator
var addressType1Value = addressComponents.EnumerateArray()
    .FirstOrDefault(a => a!.Value.GetProperty("types").EnumerateArray().Select(t => t.GetString())
                                    .Contains(address_type_1));

string uf = null;
if (addressType1Value.ValueKind != JsonValueKind.Undefined)
{
    uf = addressType1Value.GetProperty("short_name").GetString();
}

With the proposed API:

// no unnecessary casting, no ! and .Value clutter
var uf = addressComponents.EnumerateArray()
    .FirstOrNull(a => a.GetProperty("types").EnumerateArray().Select(t => t.GetString())
                                    .Contains(address_type_1))?
    .GetProperty("short_name").GetString();

@Thaina
Copy link
Author

Thaina commented Aug 13, 2024

@eiriktsarpalis Completed? Which version?

@eiriktsarpalis
Copy link
Member

I have no idea how this happened. This hasn't been addressed obviously.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api-needs-work API needs work before it is approved, it is NOT ready for implementation area-System.Linq wishlist Issue we would like to prioritize, but we can't commit we will get to it yet
Projects
None yet
Development

No branches or pull requests