How to find elements in a list with LINQ in C#

LINQ provides several methods for finding elements in a collection: First, FirstOrDefault, Last, LastOrDefault, Single, SingleOrDefault, Any, All, and Find. Choosing the right one avoids InvalidOperationException and keeps your intent clear.

The key distinction is what happens when no element matches — some methods throw, others return the type's default value (null for reference types, 0 / false for value types).

First vs FirstOrDefault

C# Example Code
var numbers = new List<int> { 1, 2, 3, 4, 5 };

// First — throws InvalidOperationException if no match
int first = numbers.First(n => n > 3);
Console.WriteLine(first); // 4

// FirstOrDefault — returns 0 (default int) if no match
int notFound = numbers.FirstOrDefault(n => n > 100);
Console.WriteLine(notFound); // 0

// FirstOrDefault with a fallback value (C# 10+)
int withFallback = numbers.FirstOrDefault(n => n > 100, -1);
Console.WriteLine(withFallback); // -1

Last vs LastOrDefault

C# Example Code
var numbers = new List<int> { 1, 2, 3, 4, 5 };

// Last — throws if no match
int last = numbers.Last(n => n < 4);
Console.WriteLine(last); // 3

// LastOrDefault — returns default if no match
int lastOrDefault = numbers.LastOrDefault(n => n > 100);
Console.WriteLine(lastOrDefault); // 0

// Without predicate
Console.WriteLine(numbers.Last());        // 5
Console.WriteLine(numbers.LastOrDefault()); // 5

Single vs SingleOrDefault

Use Single when you expect exactly one match. It throws if there are zero or more than one.

C# Example Code
var users = new List<User>
{
    new User(1, "Alice"),
    new User(2, "Bob"),
    new User(3, "Charlie")
};

// Single — throws if 0 or 2+ matches
var alice = users.Single(u => u.Id == 1);
Console.WriteLine(alice.Name); // Alice

// SingleOrDefault — returns null if 0 matches, throws if 2+ matches
var missing = users.SingleOrDefault(u => u.Id == 99);
Console.WriteLine(missing); // null (no exception)

record User(int Id, string Name);

Find (List<T> method)

Find() is a List<T> method (not LINQ) that returns the first match or default. It's slightly faster than FirstOrDefault for List<T> because it avoids LINQ overhead.

C# Example Code
var numbers = new List<int> { 1, 2, 3, 4, 5 };

// List.Find — only on List<T>
int found = numbers.Find(n => n > 3);
Console.WriteLine(found); // 4

// FindIndex — returns the index instead of the value
int index = numbers.FindIndex(n => n > 3);
Console.WriteLine(index); // 3

// FindAll — returns all matches (like Where().ToList())
var all = numbers.FindAll(n => n > 3);
Console.WriteLine(string.Join(", ", all)); // 4, 5

Any — Does Any Element Match?

C# Example Code
var numbers = new List<int> { 1, 2, 3, 4, 5 };

// Check if any match the predicate
bool hasEven = numbers.Any(n => n % 2 == 0);
Console.WriteLine(hasEven); // True

bool hasNegative = numbers.Any(n => n < 0);
Console.WriteLine(hasNegative); // False

// Without predicate — checks if sequence is non-empty
var empty = new List<int>();
Console.WriteLine(numbers.Any()); // True
Console.WriteLine(empty.Any());   // False

All — Do All Elements Match?

C# Example Code
var numbers = new List<int> { 2, 4, 6, 8, 10 };

bool allEven = numbers.All(n => n % 2 == 0);
Console.WriteLine(allEven); // True

bool allPositive = numbers.All(n => n > 0);
Console.WriteLine(allPositive); // True

bool allLarge = numbers.All(n => n > 5);
Console.WriteLine(allLarge); // False

Contains

C# Example Code
var fruits = new List<string> { "apple", "banana", "cherry" };

Console.WriteLine(fruits.Contains("banana")); // True
Console.WriteLine(fruits.Contains("grape"));  // False

// For custom objects, Contains uses Equals by default
var numbers = new List<int> { 1, 2, 3, 4, 5 };
Console.WriteLine(numbers.Contains(3)); // True

Count with Predicate

C# Example Code
var numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

int evenCount = numbers.Count(n => n % 2 == 0);
Console.WriteLine(evenCount); // 5

int largeCount = numbers.Count(n => n > 7);
Console.WriteLine(largeCount); // 3

Real-World Example: Finding a User by ID

C# Example Code
var users = new List<User>
{
    new User(1, "Alice",   "admin"),
    new User(2, "Bob",     "user"),
    new User(3, "Charlie", "user")
};

// Safe lookup — won't throw if not found
User? found = users.FirstOrDefault(u => u.Id == 2);
if (found is not null)
    Console.WriteLine($"Found: {found.Name} ({found.Role})");
// Output: Found: Bob (user)

// Check if any admins exist
bool hasAdmin = users.Any(u => u.Role == "admin");
Console.WriteLine($"Has admin: {hasAdmin}"); // True

// Are all users verified (all have a role)?
bool allHaveRole = users.All(u => !string.IsNullOrEmpty(u.Role));
Console.WriteLine($"All have role: {allHaveRole}"); // True

record User(int Id, string Name, string Role);

Comparison Table

MethodReturnsThrows when...
First(pred)First matchNo match found
FirstOrDefault(pred)First match or defaultNever
Last(pred)Last matchNo match found
LastOrDefault(pred)Last match or defaultNever
Single(pred)Exactly one match0 or 2+ matches
SingleOrDefault(pred)One match or default2+ matches
Find(pred) (List only)First match or defaultNever
Any(pred)boolNever
All(pred)boolNever
Contains(value)boolNever
Count(pred)intNever