In application development, situations where you need to verify whether the contents of two lists (arrays or collections) are identical occur frequently. Examples include consistency checks before and after data migration, or comparing expected values against actual results in unit testing.
By using the SequenceEqual method included in C# LINQ, you can determine if two sequences contain the “same elements” in the “same order.” However, the behavior differs depending on the data type being handled (value type or reference type), so a correct understanding is necessary.
This article explains how to compare basic data types, points of caution when comparing objects using custom classes, and solutions using modern C#.
Basic Behavior of the SequenceEqual Method
SequenceEqual compares two collections sequentially from the beginning and returns true only if all the following conditions are met:
- The number of elements is equal.
- The elements at corresponding indexes are equal (based on the equality determination for that type).
Practical Code Example: Comparing Numeric Data
The following code is an example of checking whether an “authorized ID list” held as a system setting matches an ID list imported from an external source. In the case of value types like int, the values themselves are compared.
using System;
using System.Linq;
namespace DataValidation
{
class Program
{
static void Main()
{
// Sequence of expected IDs
var authorizedIds = new[] { 101, 102, 103, 104, 105 };
// Sequence to verify (Correct order and values)
var importedIds = new[] { 101, 102, 103, 104, 105 };
// Sequence to verify (Different order)
var shuffledIds = new[] { 105, 104, 103, 102, 101 };
// 1. Case of an exact match
bool isExactMatch = authorizedIds.SequenceEqual(importedIds);
Console.WriteLine($"Exact match: {isExactMatch}");
// 2. Case where elements are the same but order differs
// SequenceEqual checks order strictly, so this becomes false
bool isOrderMatch = authorizedIds.SequenceEqual(shuffledIds);
Console.WriteLine($"Different order: {isOrderMatch}");
}
}
}
Execution Result
Exact match: True
Different order: False
Points of Caution When Comparing Reference Types (Classes)
When using SequenceEqual, the most important thing to watch out for is when the list contains custom classes (reference types).
For normal classes (class), equality determination defaults to “reference comparison” (checking if the memory address is the same). Therefore, even if all property values are the same, if the instances (created with new) are different, they are determined to be “not equal.”
Solution: Value-Based Equality with Record Types
Using the record type introduced in C# 9.0 allows you to solve this problem smartly. record types automatically provide value-based equality (considering objects equal if all property values are the same), so comparisons with SequenceEqual work as intended.
The following code is an example of comparing lists of product data.
using System;
using System.Collections.Generic;
using System.Linq;
namespace ProductComparison
{
// Use record type (C# 9.0+)
// This performs equality checks based on "all property values" rather than references.
public record Product(string Code, string Name, decimal Price);
class Program
{
static void Main()
{
// Master data
var masterData = new List<Product>
{
new Product("P001", "Wireless Earphones", 12000m),
new Product("P002", "Smart Watch", 25000m)
};
// Comparison data (Create instances separate from master)
// Property values are exactly the same as master
var comparisonData = new List<Product>
{
new Product("P001", "Wireless Earphones", 12000m),
new Product("P002", "Smart Watch", 25000m)
};
// Comparison using SequenceEqual
// If Product were a class, this would be False.
// Since Product is a record, it looks at value matches and becomes True.
bool areListsEqual = masterData.SequenceEqual(comparisonData);
Console.WriteLine($"Are list contents identical?: {areListsEqual}");
}
}
}
Execution Result
Are list contents identical?: True
Technical Points
1. Using IEqualityComparer
If existing design constraints prevent you from using record types, and you cannot override the Equals method on the class side, you can inject comparison logic externally by passing an implementation class of IEqualityComparer<T> to the second argument of SequenceEqual.
2. When You Want to Ignore Order
If you want to know “whether they contain the same elements as a set, regardless of order,” SequenceEqual is not suitable. In that case, effective approaches include sorting with OrderBy before performing SequenceEqual, or using HashSet to perform set comparison (SetEquals).
Summary
SequenceEqual is a method for verifying the exact match of the order and content of a list. While it works intuitively for basic types like numbers and strings, you need to be conscious of whether you are doing a “reference comparison” or “value comparison” when comparing lists of custom classes. In modern C# development, adopting record for types intended to hold data allows you to solve this equality issue simply.
