48

So I've got a collection of structs (it's actually a WCF datacontract but I'm presuming this has no bearing here).

List<OptionalExtra> OptionalExtras;

OptionalExtra is a struct.

public partial struct OptionalExtra

Now I'm running the below statement:

OptionalExtra multiOptExtra = OptionalExtras.Where(w => w.Code == optExtra.Code).FirstOrDefault();
if (multiOptExtra != null)
{

}

Now this won't compile:

the operator != cannot be applied to opperands of type OptionalExtra and '<null>'

After a little googling I realised it's because OptionalExtra is a struct. Which I believe is not nullable unless defined as a nullable type?

So my question is, if my where statement returns no results what will be the outcome of the FirstOrDefault call? Will it thrown an exception?

Incidently this should never happen but better safe than sorry.

Liam
  • 27,717
  • 28
  • 128
  • 190

7 Answers7

82

If your collection is empty, FirstOrDefault will return default(OptionalExtras). The default value of a struct is the struct with all its values in turn default initialized (i.e. zero, null, etc.).

If you assume that there will be an element and your code doesn't work with an empty collection, Use First() instead, since that will throw an exception when your collection is empty. It's generally better to fail fast than to return wrong data.

If you cannot assume that there will be an element, but also cannot deal with struct default initialization, you might make the structs in the collection a nullable value type, for example as follows:

OptionalExtras
    .Where(w => w.Code == optExtra.Code)
    .Cast<OptionalExtra?>()
    .FirstOrDefault();

This way you can get a null return even for a struct. The key idea here is to extend the set of possible values to include something other than an OptionalExtra to allow detection of an empty list. If you don't like nullables, you could instead use a Maybe<> implementation (not a .NET builtin), or use an empty-or-singleton list (e.g. .Take(1).ToArray(). However, a nullable struct is likely your best bet.

TL;DR;

  • .FirstOrDefault<T>() returns default(T) if the sequence is empty
  • Use .First() instead if you assume the list is non-empty.
  • Cast to nullable and then use .FirstOrDefault<T>() when you cannot assume the list is non-empty.
Kris Dover
  • 544
  • 5
  • 9
Eamon Nerbonne
  • 47,023
  • 20
  • 101
  • 166
9

As others have said, the result of your code when no elements match will be:

default( OptionalExtra )

If you want a null returned, you can cast your list to OptionalExtra?

OptionalExtra? multiOptExtra = OptionalExtras.Cast<OptionalExtra?>().Where( ...

You can then test for null

Nick Butler
  • 24,045
  • 4
  • 49
  • 70
  • I don't have control of the struct it's a WCF DataContract from a third party. Thanks anyway – Liam Mar 14 '13 at 10:53
  • 1
    You don't need control of the struct - you are just temporarily casting the elements of a `List` to `OptionalExtra?` which is valid LINQ – Nick Butler Mar 14 '13 at 10:55
  • I think I prefer @EamonNerbonne answer. I'd rather it fail if it doesn't exist, rather than spend power casting. In this instance a failure should be very rare if at all so it will be better to optimise towards the most likly case. Like I said, ta anyway – Liam Mar 14 '13 at 10:57
  • +1 this is the way to go when you *don't* know if the list is non-empty. – Eamon Nerbonne Mar 14 '13 at 11:06
4

If default(OptionExtra) is still a valid value, it's better to change your code to this

var results = OptionalExtras.Where(w => w.Code == optExtra.Code).Take(1).ToList();
if (results.Any()) {
   multiOptExtra = results[0]
}
Vasea
  • 5,043
  • 2
  • 26
  • 30
  • Prefer `ToArray` over `ToList` - arrays are faster, use less memory, are more restrictive (i.e. few accidental bugs), and have a shorter syntax. If you're using LINQ, there's almost never a reason to use `List<>`. – Eamon Nerbonne Mar 14 '13 at 10:58
  • 1
    @EamonNerbonne Except when you want to add items to the results. – Vasea Mar 14 '13 at 11:06
  • 2
    @EamonNerbonne This seems like an overstatement (see http://stackoverflow.com/a/1106012/83171) and smells of micro-optimization. The performance difference is insignificant between `ToArray` and `ToList` methods, `List` provides more functionality, the usage syntax is similar and it is actually safer than arrays due to their broken covariance which allows code like this `var x = new string[1]; ((object[])x)[0] = 1;`. It makes sense when you have many one-element arrays like in this case, but I wouldn't say "almost never use List with LINQ" is correct. – Vasea Mar 14 '13 at 16:55
  • The perf difference isn't in `.ToList` and `.ToArray`; but in every subsequent usage of the collection (and doubling of the GC object count). Secondly, if you're casting, you can always cause exceptions (but what you say *can* cause problems, no denying it). In practice, however, the existence of `List.Add` is a much more likely cause of problems, as is the existance of `List.Reverse` which implicitly replaces LINQ's `Reverse`. – Eamon Nerbonne Mar 14 '13 at 17:51
  • I'm **not** saying array is perfect - e.g. the covariance problem is real! But let's look a little more generally: The *normal* LINQ model is that you don't modify the underlying collection but instead return a new collection - but `List<>` makes it easy to introduce bugs when you're doing that by code that modifies a list after it's been generated. Sure you *can* do that with array too, but you're slightly less likely too. Put it this way: `List<>`'s extra functionality is actively harmful to robustness. The perf is just gravy. – Eamon Nerbonne Mar 14 '13 at 17:57
  • 1
    I get your point and I **agree** with you that arrays are more performant - they are used as the underlying collection for many other collections. And sure `List<>` has its problems. I still don't agree with your statement that there is almost never a reason to use them with LINQ. As a side note, I micro-benchmarked the `.ToArray()` and `.ToList()` methods and on my laptop the `.ToArray()` method seems to trigger more garbage collections. You can try for yourself - https://gist.github.com/vas6ili/5164182. – Vasea Mar 14 '13 at 19:14
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/26197/discussion-between-eamon-nerbonne-and-vasea) – Eamon Nerbonne Mar 14 '13 at 21:10
3

The result will be the default value of your struct, e.g. default(OptionalExtras).

Whereas for a reference type the default value is null.

Liam
  • 27,717
  • 28
  • 128
  • 190
Phil
  • 42,255
  • 9
  • 100
  • 100
2

its provide you defualt value for your structure like as below

int[] numbers = { };
int first = numbers.FirstOrDefault();
Console.WriteLine(first);//this print 0 as output 

other option to handle is make use of default value like as below

List<int> months = new List<int> { };
// Setting the default value to 1 by using DefaultIfEmpty() in the query. 
int firstMonth2 = months.DefaultIfEmpty(1).First();
Console.WriteLine("The value of the firstMonth2 variable is {0}", firstMonth2);
Pranay Rana
  • 175,020
  • 35
  • 237
  • 263
0

If you want to check for null, use System.Nullable collection:

var OptionalExtras = new List<OptionalExtra?>();
/* Add some values */
var extras = OptionalExtras.FirstOrDefault(oe => oe.Value.Code == "code");
if (extras != null)
{
    Console.WriteLine(extras.Value.Code);
}

Note that you have to use Value to access the element.

  • But a struct can never be null? – Liam Aug 28 '15 at 07:31
  • The ? notation cast the struct to a Nullable object, hence the myStructVar.Value requirement. Not sure if it's a good design to do so, but it's simple. – Nikolay Stoynov Aug 31 '15 at 07:32
  • But this doesn't answer the question. I asked what the result is of FirstOrDefault of a struct (value type). Your saying if I make my struct nullable (change it to a reference type) I'll get a null. This wasn't what I asked for. – Liam Sep 01 '15 at 08:44
  • This is not a direct answer to your question, but since you have approved (and correct) answer I added a case where you can safety check if your LINQ query is empty. – Nikolay Stoynov Sep 02 '15 at 09:36
-1

Assuming Code is a string for the purposes of my answer, you should be able just to test that value for its default.

OptionalExtra multiOptExtra = OptionalExtras.Where(w => w.Code == optExtra.Code).FirstOrDefault();
if (multiOptExtra.Code != null)
{

}
Jersey Dude
  • 527
  • 1
  • 5
  • 16
  • If the `Where` returned `null` `multiOptExtra.Code != null` would throw a `nullReferenceExcepetion`, this also doesn't actually answer the question, which is what does `firstordefault()` return for a `struct` collection. – Liam Apr 23 '14 at 09:26
  • @Liam: Null would not be returned. I just took a variation of your code, ran it, and FirstOrDefault returned an empty struct not null. See: http://www.harriergroup.com/mll/imagesonweb/struct.png – Jersey Dude Apr 23 '14 at 13:43
  • hmmm, actually your right...I forgot this was a struct. Still doesn't answer my question though. – Liam Apr 23 '14 at 13:47