Difference between Single and First in C#

In the extension methods Single / SingleOrDefault and First / FirstOrDefault have semantic distinction. Though in many places these methods are being used without the semantic consideration, more often they return the expected results. This behavior makes this misuse more common.

Both the Single and First methods have an accompanying helper method named as xxOrDefault which does nothing more than returning the default value of the specified data type if no element matches the query in the collection. For example if it’s a string collection they return null and if it’s an integer collection they return 0.

So the main semantic distinction comes between Single and First

To check this we create 3 collections like the following.

   1: private static List<StudentViewModel> list0;

   2:  

   3: private static List<StudentViewModel> list1 = new List<StudentViewModel>()

   4: {

   5:     new StudentViewModel() { StudentId = 1, StudentName = "Thuru", Gpa = 3.92},

   6:     new StudentViewModel() { StudentId = 2, StudentName = "Kristin", Gpa = 3.92},

   7:     new StudentViewModel() { StudentId = 3, StudentName = "Jack", Gpa = 3.92},

   8:     new StudentViewModel() { StudentId = 4, StudentName = "Anna", Gpa = 3.92},

   9:     new StudentViewModel() { StudentId = 5, StudentName = "Anderson", Gpa = 3.92},

  10:     new StudentViewModel() { StudentId = 6, StudentName = "Niki", Gpa = 3.92},

  11:     new StudentViewModel() { StudentId = 7, StudentName = "Jhon", Gpa = 3.92},

  12:     new StudentViewModel() { StudentId = 7, StudentName = "Brown", Gpa = 3.92},

  13: };

  14:  

  15: private static List<StudentViewModel> list2 = new List<StudentViewModel>()

  16: {

  17:     

  18: };

list0 is null, list1 and list2 are instantiated collections where list2 has no elements.

 

Single / SingleOrDefault

Single as the name specifies is used and should be used where there’s one and only element match the query.

   1: //ArgumentNullException

   2: list0.Single();

   3:  

   4: //ArgumentNullException

   5: list0.SingleOrDefault();

   6:  

   7: // InvalidOperationException - Sequence contains no elements

   8: list2.Single();

   9:  

  10: // works return null if there's no elements matching the criteria

  11: StudentViewModel st = list2.SingleOrDefault();

  12:  

  13:  

  14: // InvalidOperationException - Sequence contains more than one element

  15: // if collection has only one element it returns that element

  16: Console.WriteLine(list1.Single());

  17:  

  18: // works

  19: Console.WriteLine(list1.Single(e => e.StudentId == 1));

  20:  

  21: // InvalidOperationException - Sequence contains more than one matching element

  22: Console.WriteLine(list1.Single(e => e.StudentId == 7));

  23:  

  24: // InvalidOperationException - Sequence contains no matching element

  25: Console.WriteLine(list1.Single(e => e.StudentId == 8));

  26:  

  27: StudentViewModel st1;

  28:  

  29: // InvalidOperationException - Sequence contains more than one element

  30: st1 = list1.SingleOrDefault();

  31:  

  32: //works

  33: st1 = list1.SingleOrDefault(e => e.StudentId == 1);

  34:  

  35: // InvalidOperationException - Sequence contains more than one matching element

  36: st1 = list1.SingleOrDefault(e => e.StudentId == 7);

  37:  

  38: // works st1 is null

  39: st1 = list1.SingleOrDefault(e => e.StudentId == 8);

 

First / FirstOrDefault

First has no issues when there’re more than one element matches the query, it simply returns the first element. But if there’re no element matching the query it prompts.

   1: // ArgumentNullException

   2: list0.First();

   3:  

   4: // ArgumentNullException

   5: list0.FirstOrDefault();

   6:  

   7: // InvalidOperationException - Sequence contains no elements

   8: list2.First();

   9:  

  10: // works return null if there's no elements matching the criteria

  11: StudentViewModel st = list2.FirstOrDefault();

  12:  

  13:  

  14: // returns the first element of the collection

  15: Console.WriteLine(list1.First());

  16:  

  17: // works

  18: Console.WriteLine(list1.First(e => e.StudentId == 1));

  19:  

  20: // returns the first element of the collection matches the query

  21: Console.WriteLine(list1.First(e => e.StudentId == 7));

  22:  

  23: // InvalidOperationException - Sequence contains no matching element

  24: Console.WriteLine(list1.First(e => e.StudentId == 8));

  25:  

  26: StudentViewModel st1;

  27:  

  28: // returns the first element of the collection

  29: st1 = list1.FirstOrDefault();

  30:  

  31: // works

  32: st1 = list1.FirstOrDefault(e => e.StudentId == 1);

  33:  

  34: // returns the first element of the collection matches the query

  35: st1 = list1.FirstOrDefault(e => e.StudentId == 7);

  36:  

  37: // works st1 is null

  38: st1 = list1.FirstOrDefault(e => e.StudentId == 8);

 

The code snippets and the comments describes the functions of the Single and First extension methods and their helping counter parts xxOrDefault.

 About the semantic distinction.

If you know that your query should return only one element it’s better to use Single or SingleOrDefault. For example when you query a customer collection by customer ID you know that according to the business there’s only one customer ID with a specific value. First or FirstOrDefault could be used in the scenario but it doesn’t give you the semantics of the business logic.

And Single or SingleOrDefault throws an InvalidOperationException with the message ‘sequence contains more than one element’ if the collection has more than one element matching the query. This behavior sometimes can be used to validate.

Another common misuse of the above extension methods is chaining them unnecessarily with ‘where’ statement like this

   1: // this is a common misuse

   2: list1.Where(e => e.StudentId == 7).Single();

   3:  

   4: list1.Single(e => e.StudentId == 7);

 

If you want the code for the demonstration purpose you can download it here

Advertisements

Comments are closed.

Powered by WordPress.com.

Up ↑

%d bloggers like this: