Confusion with nullability of a custom LINQ method

I have this custom extension method I used a lot in my project, however every time I use it, it trips nullability checks and thinks I'm using a possibly null value.
/// <summary>
/// Tries to get the first item that meets a condition from a collection.
/// </summary>
/// <typeparam name="T">Type of items in the collection</typeparam>
/// <param name="source">Collection to get the first item of</param>
/// <param name="predicate">Method that returns <see langword="true"/> if a given item meets the condition</param>
/// <param name="item">Found item</param>
/// <returns><see langword="true"/> if an item was found</returns>
public static bool TryGetFirst<T>(this IEnumerable<T> source, Predicate<T> predicate, [MaybeNullWhen(false)] out T item)
{
ArgumentNullException.ThrowIfNull(predicate);

foreach (T t in source)
if (predicate(t))
{
item = t;
return true;
}

item = default;
return false;
}
/// <summary>
/// Tries to get the first item that meets a condition from a collection.
/// </summary>
/// <typeparam name="T">Type of items in the collection</typeparam>
/// <param name="source">Collection to get the first item of</param>
/// <param name="predicate">Method that returns <see langword="true"/> if a given item meets the condition</param>
/// <param name="item">Found item</param>
/// <returns><see langword="true"/> if an item was found</returns>
public static bool TryGetFirst<T>(this IEnumerable<T> source, Predicate<T> predicate, [MaybeNullWhen(false)] out T item)
{
ArgumentNullException.ThrowIfNull(predicate);

foreach (T t in source)
if (predicate(t))
{
item = t;
return true;
}

item = default;
return false;
}
Example usage causing the false positive
if (collection.TryGetFirst(item => item.IsActive, out Item item)
{
}
if (collection.TryGetFirst(item => item.IsActive, out Item item)
{
}
Here, the warning comes at the out parameter. If I make the out in the usage nullable, then the generic type takes the nullabiltiy and the warning moves to when I use the item inside the if. collection is IEnumerable<Item> (not nullable)
10 Replies
canton7
canton73w ago
Hmm. It's OK if you force the generic type:
if (a.TryGetFirst<string>(x => x.StartsWith("a"), out string? v))
if (a.TryGetFirst<string>(x => x.StartsWith("a"), out string? v))
TheBoxyBear
TheBoxyBearOP3w ago
Kind of a hack though
canton7
canton73w ago
So the problem is that it's using type inference to infer T from the nullability of out Item, and then that's feedback back to say that item can be null regardless of the return value
TheBoxyBear
TheBoxyBearOP3w ago
Unless I add the notnull constraint and make the out nullable But it may cause some issues with structs
canton7
canton73w ago
public static bool TryGetFirst<T>(this IEnumerable<T> source, Predicate<T> predicate, [NotNullWhen(true)] out T? item)
public static bool TryGetFirst<T>(this IEnumerable<T> source, Predicate<T> predicate, [NotNullWhen(true)] out T? item)
That almost works, except that it insists that item is not null even when T is a nullable type Nah, T? only means Nullable<T> when T is constrained to struct, otherwise it means defaultable, i.e. you can do T item = default Heh, OK, the inference is fine if you do out var https://lab.razor.fyi/#fVLbitswEKUU9kGP_YLBTzYE_0A2gSWbXVKyZWENeQiBlZWxPVSWUmmUJhR_Zf-gX1LsONey1ZN15hzNnDMWf-6EeHW2dLJOlf_y-y54MiW87T1jPRSXt3RitUbFZI1Pn9GgI_UfxqyuA8tc4w3nkWRprGdS_uNKOrFrfDBS7z3d0uZkftxAWeVQrsmUH-FpJv13PxRCaek99I7FLwEA4FkyKdhaWsOLJBMnHXwoHgiOTLlcgYQRLCMZDSDKo9XwRKACYplmbv-M_ETOc7yD0Rh26RtLx35BXMWRjJIB2MCwlQ62yaHJdaP2TKzxVmO6cMQ4J4PxNp2jKblKzg2b7qsRjRCbkGtSRxMHf9Mdo_HtGnqL16TcWg0X095n45gr8jCbmlCja7d2n43B2-AUDuDV4ZqU5A7cHC8DWL7IfY7fgtaLCk1cSO0xWXUeMyDG-jbIB1eGGg23kulO4ab9Vdot2Z-zogXj0-vJUJxkhXUoVQVxBgxk-rnOAR5XcBLHfBHvvxF3fMYaRsDnTI_HIQdngF3A62JznqhXr7GQQV-80Wu7JA5oI5qvn10w75_-Ag The way you wrote it is the correct way to write it, I think. It's just annoying that the type inference gets it wrong Might be worse cross-posting to #roslyn ?
TheBoxyBear
TheBoxyBearOP3w ago
So maybe with that approach but having overloads with the nulla and notnull constraints Although it seems I can't have such overloads even with the class? constraint on the other that should eliminate overlaps
canton7
canton73w ago
Yeah, you can't annoyingly
TheBoxyBear
TheBoxyBearOP3w ago
c++ brain scream So you just can't have nullability-based overloads, meanwhile other types of constraints are ok :Hmm:
Unknown User
Unknown User3w ago
Message Not Public
Sign In & Join Server To View
TheBoxyBear
TheBoxyBearOP3w ago
I'm not too sure how this addresses the issue given everything in your example is public Also the nullability comes through the out parameter that may be null if returning false But since it's called as part of an if, logically the out is safe to use within that block, even if T is nullable

Did you find this page helpful?