Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

Use the built-in aggregate operators (Sum, Count, Average, Min, Max) when they state your intent directly. Reach for Aggregate when the calculation is a custom reduction, because it lets you define exactly how each element changes the accumulated value. The built-ins tell a reader what is being computed; Aggregate shows how.

What the built-in operators cover, and where they stop

Microsoft groups the aggregation operators in LINQ into Aggregate, Average, Count, LongCount, Max (and MaxBy), Min (and MinBy), and Sum. Each of them computes one value from a collection. Only Aggregate is a general-purpose operation: you supply the logic. The others have fixed meanings, which is their strength. A reader who sees orders.Sum(o => o.Total) understands the code at a glance.

Two details matter when you compare them. First, Aggregate has no C# query-expression syntax, so you always call it as a method, for example numbers.Aggregate(...). Second, the built-ins already cover the common numeric and counting cases, so Aggregate is most useful for the reductions that do not fit any of them.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The three Aggregate overloads

The overloads differ in three ways: whether you pass a seed, whether you pass a result selector, and what happens when the sequence is empty.

Overload Initial accumulator Result selector Empty input
Aggregate(func) The first element of the source No Throws InvalidOperationException
Aggregate(seed, func) The seed you pass No Returns the seed, because no element is applied
Aggregate(seed, func, resultSelector) The seed you pass Yes; maps the final accumulator to a result of another type Runs the result selector on the seed

The empty-input column for the seeded overloads follows from how the seed works. The Microsoft Learn reference for Enumerable.Aggregate states: “The value of the seed parameter is used as the initial aggregate value.” Because the accumulator never runs on an empty sequence, the seed is the value that comes back. The reference page linked here is the .NET 5 view, so check the current version of the page for any later overloads.

Worked example 1: a conditional count with a seed

Microsoft’s own example counts even integers. The seed is 0, the accumulator adds one for each even value, and the total is returned:

int[] ints = { 4, 8, 8, 3, 9, 0, 7, 8, 2 };
int numEven = ints.Aggregate(0, (total, next) =>
    next % 2 == 0 ? total + 1 : total);
// numEven is 6

A Where(...).Count() chain gives the same answer here and is easier to read. The reason to choose Aggregate is that the accumulator can carry state that a count cannot.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Worked example 2: a single pass for minimum and maximum

Calling Min() and Max() on the same array walks it twice. A tuple seed lets one Aggregate call track both values:

double[] readings = { 12.5, 9.1, 15.0, 7.4 };
var (low, high) = readings.Aggregate(
    (Min: double.MaxValue, Max: double.MinValue),
    (acc, x) => (Math.Min(acc.Min, x), Math.Max(acc.Max, x)));
// low = 7.4, high = 15

The seed here is chosen so that any real value replaces it. Because the seed is returned for an empty array, low and high would be double.MaxValue and double.MinValue, not an exception. If an empty input should be treated as an error or as “no value”, check for that before calling Aggregate, or use the result selector in the next example.

Worked example 3: a mean with a result selector

The result selector lets the accumulator hold intermediate state, such as a running sum and count, and turns it into a final value of a different type at the end:

double[] scores = { 72, 85, 91 };
double mean = scores.Aggregate(
    (Sum: 0.0, Count: 0),
    (acc, x) => (acc.Sum + x, acc.Count + 1),
    acc => acc.Count == 0 ? double.NaN : acc.Sum / acc.Count);
// mean is approximately 82.67

For a plain average, scores.Average() is the better choice. This example shows the pattern, which becomes useful when the final step is not a built-in, such as formatting a summary or building a result object.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The unseeded overload: a common mistake

The overload without a seed uses the first element as the starting accumulator, and that element is not passed through your delegate. Microsoft warns that this can produce the wrong result when you apply a condition, such as summing only the even values:

int[] nums = { 3, 2, 4 };
int wrong = nums.Aggregate((acc, n) => n % 2 == 0 ? acc + n : acc);  // 9
int right = nums.Aggregate(0, (acc, n) => n % 2 == 0 ? acc + n : acc); // 6

In the first call, the leading 3 is the starting accumulator, so it is added without being tested. The fix is to pass an explicit seed. Use the unseeded overload only when the first element is a valid starting value and the sequence is known to be non-empty.

Choosing between a built-in and Aggregate

  • Use the named operator when the task is a standard sum, count, average, minimum, or maximum. The intent is visible from the method name.
  • Use a seeded Aggregate when you need a custom accumulation, several values computed in one pass, or a final transformation through a result selector.
  • Use the unseeded overload only when the first element is a correct starting point and empty input is impossible or should throw.
  • Check the execution context before you rely on the result. Behavior over IEnumerable<T> in memory is not the same as behavior through a database provider.

LINQ to Objects versus provider-backed queries

The examples above run in memory through IEnumerable<T>. When the source is an IQueryable<T>, such as a LINQ to Entities query, the provider translates the expression into the data source’s language, and results can depend on that translation.

  • Null handling follows the data source. Microsoft’s LINQ to Entities reference gives an example in which SQL Server’s Sum ignores nulls. Other backends can behave differently, so do not assume that behavior carries over.
  • Conversions and precision can change the result. The same page warns that server-side conversions and precision loss can make Sum or Average differ from what you expect from CLR arithmetic.
  • Not every operator or overload is supported. Microsoft points readers to the provider’s list of supported and unsupported methods. A custom accumulator delegate has no obvious server-side equivalent, so expect a translation failure or client-side evaluation, and confirm which one applies to your provider and version.

When the result matters, log or inspect the generated SQL, compare the output with a small in-memory test, and test with null values and an empty table.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Where to go next

Microsoft’s aggregation overview is at Aggregation operations (C#). That page is dated 2021-09-15, so check it against the current .NET documentation for newer members such as MinBy and MaxBy. The LINQ to DataSet examples in Method-Based Query Syntax Examples: Aggregate Operators (LINQ to DataSet) show Aggregate building a comma-separated list of contact last names, which is a useful pattern for string reductions.

Provider semantics are covered in Standard Query Operators in LINQ to Entities Queries, last updated 2021-09-15. Read it alongside your provider’s own documentation, because the behavior it describes is specific to LINQ to Entities.

The rule to take away is simple. Use Sum, Count, and their companions when they say what you mean. Use Aggregate when the reduction itself is the logic, give it an explicit seed, and verify the result against the provider that will run the query.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.