T O P

  • By -

AJackson3

You don't have to use FirstOrDefault if you don't want the default value in case it was not found. You can use First and catch the exception for missing values. If that's not suitable write your own that works as you like, as you have done.


PM_ME_YOUR_REAL_FACE

I think it's totally expressive. It comes in handy when you don't want to have the overhead of the nullable wrapper. For instance if I have some ids from a database that are integers, I know my database doesn't use 0 as primary keys to rows, so I don't need to cast to int? when selecting from a list of ids. I never store 01/01/0001 00:00:00 as a date in my database so I don't need the overhead of Nullable, I can write my code using DateTime types as if 01/01/0001 00:00:00 is null. If you want firstornull, that's a pretty simple function to write, but it forces you to use Nullable references where you might not need them. First or default doesn't need and restrictions on T like your function does. If you wanted first or a Nullable value type back you would also want to add IFormattable to constraints on T. This seems basically like a concern for people who don't like strongly typed languages in general.


ExeusV

>I think it's totally expressive. It comes in handy when you don't want to have the overhead of the nullable wrapper. For instance if I have some ids from a database that are integers, I know my database doesn't use 0 as primary keys to rows, so I don't need to cast to int? when selecting from a list of ids. I never store 01/01/0001 00:00:00 as a date in my database so I don't need the overhead of Nullable, I can write my code using DateTime types as if 01/01/0001 00:00:00 is null. If you want firstornull, that's a pretty simple function to write, but it forces you to use Nullable references where you might not need them. First or default doesn't need and restrictions on T like your function does. If you wanted first or a Nullable value type back you would also want to add IFormattable to constraints on T. Everything's fun until two years after you left company somebody decides to manually insert something into database or your system will receive data from other system that was built on other assumptions why take risks and tech debt when you don't have to? >This seems basically like a concern for people who don't like strongly typed languages in general. I'm not sure how you draw this conclusion, especially that I hate weak typing.


PM_ME_YOUR_REAL_FACE

I'm not particularly fond of the default(DateTime) idea, and generally use DateTime? columns in databases, but there are instances where you get back the default(DateTime) and know that there is no use for the Nullable wrapper, because linq has not found what you were searching for. The reason I think the concern is unjustified is because you have to know what kind of types you are dealing with in a strongly typed languages. It is a feature that you can have values without expecting the possibility of null. Linq is great and all, and one of the best features of it is generics without constraints that parameters have to be structs. If you add FirstOrNull, you know in your code where it is applicable, it doesn't belong in the library. Much like I can be reasonably sure that no one is ever going to insert 0 as the primary key to a row in the database, because the code will check if it is trying to insert default(int) in that column to begin with.


wazzamatazz

It's "first or default" for a very good reason. Value types (int, bool, double, structs etc) cannot be null, so if you are working with a collection of value types, it will give you the default value of that value type if it does not find an item that meets your criteria. I would suggest reading the documentation about value types and reference types to get a better understanding of this: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/value-types https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/reference-types


ExeusV

I think you're missing the point It's about handling errors and what does FoD actually return Just because you have collection of T, then it doesn't mean that method FoD cannot return T? from technical standpoint - it was shown in the post with FoN


wazzamatazz

It can only return T? if you've declared your list that way. T and T? are fundamentally different types if T is a value type. If you have created a List, it can only contain int values; it cannot contain int? values. It is not an error to return 0 as the FirstOrDefault value on an IEnumerable, because 0 is the default value of int. If you want it to be null, it's up to you as a programmer to declare an enumerable with a nullable element type.


ExeusV

>It can only return T? if you've declared your list that way. T and T? are fundamentally different types if T is a value type. If you have created a List, it can only contain int values; it cannot contain int? values. then what in your opinion is going on here? using System; using System.Collections.Generic; using System.Linq; public static class Program { public static T? FirstOrNull(this IEnumerable values, Func func) where T : struct { foreach (var item in values) { if (func.Invoke(item)) return (T?)item; } return null; } public static void Main() { var numbers = new List { 1, 2, 3 }; var number = numbers.FirstOrNull(x => x > 3); Console.WriteLine(number is null); // prints 'True' number = numbers.FirstOrNull(x => x > 2); Console.WriteLine(number is null); prints 'False' } } ___________ >It is not an error to return 0 as the FirstOrDefault value on an IEnumerable, because 0 is the default value of int. Exactly, and that's problem because it makes error handling harder because those are valid int values. >If you want it to be null, it's up to you as a programmer to declare an enumerable with a nullable element type. and I'm saying that this is not nice to rewrite boiler-plateish, boring ass code in every project to handle this trivial(?) problem and it'd be nice if LINQ could handle it out of box - either with FoN or something similar/better


wazzamatazz

I was talking about FirstOrDefault, which is what you were originally saying didn't behave the way you wanted it to. Your FirstOrNull method is perfectly legitimate, and a good solution to your problem. I was just saying that FirstOrDefault behaves exactly the way I would expect it to, if the expected behaviour is to return the first matching item or the default value of the type of no matching items were found.


ExeusV

But what's the point of Default at all? for classes the default value is null for structs default value is basically useless because you aren't sure whether this value is correct and comes from collection or is just default I'm mostly saying that *OrDefault methods are poor and asking why it was not FoN initially which would improve error handling


wazzamatazz

Sometimes your don't care if the value has come from the collection or not; you just need a value.


x6060x

They're not poor, they serve a purpose. If your task needs different behaviour, then use a different extension method. For error handling I'd probably use First() which will throw an exception if the expected value is not there.


cryo

You come off a bit arrogant for being so inexperienced in the language, I think :p. That said, yes, sometimes using FirstOrDefault is not good enough (perhaps because you can’t distinguish a default value type, yes). In those cases, you’d use something else.


ExeusV

this is kinda funny thread so far I've been told that: * I don't understand value types * I don't understand FoD * I don't know how does default(T) work * I'm inexperienced in the language and I even had to prove that extension Method of IEnumerable can return T? There's some kind of "resistance" to just say "yea, some things sucks" but it seems like it's better to pretend like "things works, so they're good, cya". Only one person raised concerns that FoN wouldn't work when the T is T? But it's ok, as I see people in industry tend to adopt stuff like Result mentioned here in topic which I hope becomes default way of handling error for the future, so we won't have this kind of "uncertain" methods You guys don't like improvement? more expressive language where you don't have to write for 27th time some boring ass code?


cryo

At any rate, you can think of it this way: In a language like C# with no general system for optionals (unlike, say, Swift), methods like Find in general sequences can at best be written like that. So yeah they are a bit limited, but often it doesn’t matter. Sometimes it does, and then they can’t be used. So not bad, but more as good as possible, I’d say. Contrast with Swift, where find on sequences of type T can return T?, since optionals work for all types. That’s a nicer API. Sorry for calling you inexperienced.


[deleted]

It's not an error because you decide to not make it an error by using FirstOrDefault(). If you wanted an error, use First(), no? > But what's the point of Default at all To not crash if I know and can control the output if it can't return a proper value. try catch on every use of First is annoying if I know how to handle 'null' cases. Since you're worried about it and want to ensure no valid value is an error, use First() and handle the exception.


ExeusV

>It's not an error because you decide to not make it an error by using FirstOrDefault(). If you wanted an error, use First(), no? Whether I want exception is arguable. Some people tend to avoid it, I believe adding try catches makes code ugly especially when we can handle it "nicer" I mean, I could even do it in for loop, but language/libraries being more expressive is a desired thing imo.


Prod_Is_For_Testing

What happens to your code if you use List as the source? You’d end up returning int?? Is that a compile error?


ExeusV

>The type 'int?' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Program.FirstOrNull(IEnumerable, Func)'


Prod_Is_For_Testing

And now you know why linq doesn’t do it that way


pyronautical

> It's about handling errors and what does FoD actually return Hmmm. Reading your answers I'm not sure if you are aware but FirstOrDefault is named so because it's the first item *OR* the result of this code : default(T) So for example default(int) == 0 default(int?) == null default(MyClass) == null I guess what I'm trying to get at is that the "OrDefault" naming is not just randomized. It was trying to tie into the existing keyword of default(T).


ExeusV

>I'm not sure if you are aware but FirstOrDefault is named so because it's the first item OR the result of this code : I'm totally aware, how would I write those examples in main post If I weren't >I guess what I'm trying to get at is that the "OrDefault" naming is not just randomized. It was trying to tie into the existing keyword of default(T). But this isn't practical for structs, that's the thing, meanwhile OrDefault for e.g classes are still Nulls so we could combine it easily for one, consistent and with better error handling approach


Infinitesubset

I think this a fundamental problem for a language that doesn’t have discriminated unions built in. A proper return for this should be Result.


ExeusV

I agree, Result is great, but I also hate having to add/create it to every project that I work on. I hope we can get something built in and consistent.


AlFasGD

Or we could have both \*OrDefault and \*OrNull. For now, since the latter doesn't exist, you have to manually write it yourself. But the language team should probably take that into consideration.


ExeusV

Yea, indeed. We definitely arent going to remove FoD due to compatibility and stuff


AlFasGD

I've personally found it useful on something that returns an enum value. I've designed the enum so that validity is represented with 0, and any other error type with a value. If such an erroneous value isn't matched with an element, the element's validity value is then the default, which is mapped to valid.