C# LINQ Average: Syntax and Edge Cases
c# linq average: Learn how to use LINQ Average in C#: syntax, numeric type behavior, empty sequence handling, selector projections, and performance considerations.
c# linq average requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.
The Average method in C# LINQ computes the arithmetic mean of a numeric sequence, but its behavior depends heavily on the element type of the sequence you call it on. It is an extension method on IEnumerable<T> defined in System.Linq, and the overloads differ in return type, empty-sequence handling, and overflow behavior.
Basic Syntax and Return Types
The simplest call looks like this:
using System; using System.Linq; int[] scores = { 82, 91, 74, 88, 95 }; double average = scores.Average(); Console.WriteLine(average); // 86
The return type is not always the same as the element type. Average on IEnumerable<int> returns double, because integer division would truncate the fractional part. The same applies to long sequences. For float sequences the result is float, and for decimal sequences it is decimal.
| Element type | Return type | Reason |
|---|---|---|
int | double | Preserves fractional precision |
long | double | Preserves fractional precision |
float | float | Matches the input precision |
double | double | Matches the input precision |
decimal | decimal | Matches the input precision |
If you need the result as a decimal from an int sequence, cast the elements first: scores.Select(x => (decimal)x).Average(). That matters when the mean feeds into financial calculations where double rounding is unacceptable.
Empty Sequences Throw an Exception
Calling Average on an empty non-nullable sequence throws InvalidOperationException. The arithmetic mean of zero values is undefined, and LINQ chooses to surface that as an error rather than silently returning a default.
int[] empty = Array.Empty<int>(); double result = empty.Average(); // InvalidOperationException
The nullable overloads behave differently. If the sequence element type is int?, double?, decimal?, and so on, an empty sequence returns null instead of throwing.
int?[] empty = Array.Empty<int?>(); double? result = empty.Average(); // null
This asymmetry is the most common source of bugs when code switches between nullable and non-nullable collections. If you cannot guarantee a non-empty sequence, either check Any() first or use the nullable overload and handle null explicitly.
Using a Selector to Average Object Properties
When the sequence contains objects rather than numbers, pass a selector that extracts the numeric value:
public record Order(int Id, decimal Amount); List<Order> orders = new() { new(1, 49.99m), new(2, 129.00m), new(3, 24.50m) }; decimal averageOrderAmount = orders.Average(o => o.Amount);
The selector is applied to every element before the mean is computed. This is equivalent to orders.Select(o => o.Amount).Average() but avoids materializing an intermediate sequence. The same overload exists for every numeric type, so Average(o => o.Amount) returns decimal when Amount is decimal, and double when Amount is int.
Nullable Elements and Null Filtering
For sequences of nullable numeric types, Average skips null values rather than treating them as zero. This is a deliberate design choice: a missing value should not drag the mean toward zero.
int?[] measurements = { 10, null, 20, null, 30 }; double? mean = measurements.Average(); // 20
The result is 20 because the two null entries are ignored and the three valid values are averaged. If you intended null to count as zero, you must map it explicitly before calling Average:
double mean = measurements.Select(m => m ?? 0).Average();
This distinction matters in reporting code where missing sensor readings or absent user inputs should not silently distort the aggregate.
Overflow and Precision Behavior
Average performs the summation internally using checked arithmetic for integer types. An int sequence whose values sum beyond int.MaxValue throws OverflowException during the addition, even though the final mean would fit comfortably in a double. The same applies to long sequences.
For floating-point types, the accumulation order affects the result. Summing a large number of small values after a few large values can lose precision because floating-point addition is not associative. If the sequence is large and the values vary widely in magnitude, consider summing in decimal or using a compensated summation algorithm before dividing.
Performance Considerations
Average makes a single pass over the sequence and performs one addition per element plus one division at the end. For in-memory collections this is O(n) with minimal overhead. The main cost to watch is the selector: a selector that performs expensive work per element multiplies that cost across the entire collection.
For LINQ to SQL or LINQ to Entities, Average translates to the SQL AVG aggregate and executes in the database. The translation only works when the query provider supports it, and the return type follows the database column type. Materializing the sequence first with ToList() and then calling Average in memory changes the semantics: the database sends every row to the client, and the mean is computed locally. That is rarely desirable for large tables.
When Average Is Not the Right Tool
Average computes the arithmetic mean. If the data contains extreme outliers, the mean can be misleading, and a median or trimmed mean would be more representative. LINQ does not provide a built-in median, so you would sort the sequence and pick the middle element, or use a statistical library.
For grouped averages, combine GroupBy with Average:
var averageByCategory = orders .GroupBy(o => o.Category) .Select(g => new { Category = g.Key, AverageAmount = g.Average(o => o.Amount) });
This produces one mean per group in a single pass over the grouped data. The grouping itself is the dominant cost, so if the data is already sorted by the grouping key, a manual loop that tracks running sums per category can be faster and use less memory.