Codectionary / Developer documentation / C#

LINQ Aggregation: Sum, Count, Average

LINQ provides built-in aggregation methods that reduce a sequence down to a single summary value: Count() for the number of elements, Sum() for a total, Average() for the mean, and Max()/Min() for extremes. Aggregate() is the general-purpose version, letting you define fully custom accumulation logic when the built-in methods aren't enough.

Syntax

collection.Sum() / .Count() / .Average() / .Max() / .Min()

Examples

Basic Aggregations

The most common summary operations.

List<int> scores = new List<int> { 85, 92, 78, 90, 88 };

Console.WriteLine(scores.Count());    // 5
Console.WriteLine(scores.Sum());       // 433
Console.WriteLine(scores.Average());   // 86.6
Console.WriteLine(scores.Max());        // 92
Console.WriteLine(scores.Min());        // 78

Aggregating with a Selector

Most aggregation methods accept a selector, letting you aggregate a property of complex objects directly.

record Product(string Name, double Price);

List<Product> cart = new List<Product>
{
    new Product("Keyboard", 25),
    new Product("Mouse", 15),
    new Product("Monitor", 200)
};

double total = cart.Sum(p => p.Price);
Console.WriteLine(total);  // 240

Product mostExpensive = cart.MaxBy(p => p.Price)!;
Console.WriteLine(mostExpensive.Name);  // Monitor

Aggregate(): Custom Accumulation

The general-purpose reduction method for logic the built-in aggregators don't directly cover.

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

int product = numbers.Aggregate(1, (acc, n) => acc * n);
Console.WriteLine(product);  // 120

string combined = numbers.Aggregate("", (acc, n) => acc + n + "-");
Console.WriteLine(combined);  // 1-2-3-4-5-

Counting with a Condition

Count() can accept a predicate directly, avoiding a separate Where() step.

List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8 };

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

Best practices

  • Use the selector overload (Sum(x => x.Property)) directly rather than chaining a separate Select() first - it is more concise and avoids an intermediate sequence
  • Use Count(predicate) instead of Where(predicate).Count() to skip creating an intermediate filtered sequence
  • Reach for Aggregate() only when the built-in Sum/Count/Average/Max/Min genuinely don't cover your accumulation logic - it is less readable for simple cases
  • Handle empty sequences carefully - Average(), Max(), and Min() throw an exception on an empty sequence, so check .Any() first or use the null-returning "OrDefault" variants where available

At a glance

Purpose
Applications on the .NET platform
File extension
.cs
Runs in
.NET runtime
Usually used with
.NET SDK and libraries

Specifications & further reading

Related C# documentation

Lambda Expressions
A lambda expression is a compact way to represent an anonymous function - a block of code that can be passed around as a value, using the => (goes to) operator. Lambdas work with delegate types and functional interfaces like Func<> and Action<>, letting you write inline logic without a full named method. Lambdas are the foundation that makes LINQ's fluent, expressive query syntax possible.
LINQ Basics: Where & Select
LINQ (Language Integrated Query) provides a consistent, declarative way to query collections, databases, and XML directly within C# syntax. Where() filters a sequence down to elements matching a condition, and Select() transforms each element into something new (a projection). Both are 'lazy' - they build a query that only actually runs when you iterate over the result or call something like ToList().
LINQ Ordering & Grouping
OrderBy() and OrderByDescending() sort a sequence by a key you specify, with ThenBy() for secondary sort keys on ties. GroupBy() splits a sequence into groups based on a key selector, producing a sequence of groups where each group itself is enumerable and has a Key property identifying which group it represents.
LINQ Element & Quantifier Operators
Element operators retrieve a single item from a sequence: First()/FirstOrDefault() for the first match, Single()/SingleOrDefault() for exactly one match, with 'OrDefault' variants returning a default value instead of throwing when nothing matches. Quantifier operators return a bool describing the sequence as a whole: Any() checks if at least one element matches, and All() checks if every element matches.