LINQ Set Operations in C# Explained

Set operations in LINQ enable developers to manipulate and compare collections using principles of mathematical set theory. This article breaks down the four fundamental LINQ set operators in C#—Distinct, Union, Intersect, and Except. You will learn how each operator functions, how duplicates are handled, and how to apply them effectively to both primitive data types and custom objects in your applications.


Understanding LINQ Set Operations

LINQ set operations evaluate collection elements based on equality. By default, primitive types (like int or string) are evaluated by value, while reference types are evaluated by reference unless IEquatable<T> is implemented or a custom IEqualityComparer<T> is provided.


1. Distinct

The Distinct operator removes duplicate elements from a sequence, returning an unordered collection of unique values.

List<int> numbers = new() { 1, 2, 2, 3, 4, 4, 5 };

IEnumerable<int> uniqueNumbers = numbers.Distinct();

// Output: 1, 2, 3, 4, 5

Starting in .NET 6, you can also use DistinctBy to filter objects based on a specific property without needing a custom equality comparer:

var uniqueUsers = users.DistinctBy(u => u.Email);

2. Union

The Union operator combines two collections into a single collection and automatically excludes duplicate elements. This differs from Concat, which joins collections without removing duplicates.

List<int> setA = new() { 1, 2, 3 };
List<int> setB = new() { 3, 4, 5 };

IEnumerable<int> result = setA.Union(setB);

// Output: 1, 2, 3, 4, 5

Like Distinct, .NET 6 introduced UnionBy, allowing you to combine two collections based on a specific key selector.


3. Intersect

The Intersect operator returns only the elements that exist in both source collections, discarding any items unique to either collection.

List<string> groupA = new() { "Alice", "Bob", "Charlie" };
List<string> groupB = new() { "Bob", "Charlie", "David" };

IEnumerable<string> commonNames = groupA.Intersect(groupB);

// Output: Bob, Charlie

Use IntersectBy when comparing complex collections based on a key property rather than the entire object.


4. Except

The Except operator produces the set difference of two collections. It returns all elements from the first collection that do not appear in the second collection.

List<int> allTasks = new() { 1, 2, 3, 4, 5 };
List<int> completedTasks = new() { 2, 4 };

IEnumerable<int> pendingTasks = allTasks.Except(completedTasks);

// Output: 1, 3, 5

Note that the order of collections matters. Calling setA.Except(setB) produces different results than setB.Except(setA).


Summary of Set Operators

Operator Purpose Duplicate Handling
Distinct Filters out duplicate values from a single collection Returns only unique values
Union Combines two collections Excludes duplicate values
Intersect Finds shared values present in both collections Excludes duplicates in the result
Except Finds values in collection A that are absent from collection B Excludes duplicates in the result