Aggregate,真正的聚合 LinQ 运算符





4.00/5 (7投票s)
回顾 LinQ 聚合运算符,并展示所有运算符都可以使用 Aggregate 运算符构建。
引言
Aggregate LinQ 运算符在 LinQ 世界中是一个未知数。该项目的目标是回顾 LinQ 聚合运算符 (Count
, LongCount
, Min
, Max
, Sum
和 Average
),并 展示所有运算符都可以使用 Aggregate 运算符构建。
目录
Count
和LongCount
Sum
Min y Max
Average
Aggregate
之前的运算符 Aggregate 实现
Sum
最小值
最大值
Average
Count 和 LongCount
扩展方法 Count
和 LongCount
返回序列的元素数量。Count
和 LongCount
之间的区别在于返回类型。Count
运算符返回一个 int
(System.Int32
) 类型,并且您的限制是 2,147,483,647
个元素。LongCount
运算符返回一个 Long
(System.Int64
) 类型,并且您的限制远大于 Count
运算符 9,223,372,036,854,775,807
个元素。对于日常使用,Count
运算符应该足够了。
签名
public static int Count<TSource>(this IEnumerable<TSource> source);
public static int Count<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
public static long LongCount<TSource>(this IEnumerable<TSource> source);
public static long LongCount<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
它们都有两个重载。第一个重载是一个简单的方法,用于计算序列中的所有元素。第二个重载有一个谓词参数 (Func<TSource, bool>
),用于仅计算序列的一部分。
示例
public static void CountAndLongCountTests()
{
var elements = new List<Element>()
{
new Element{ ID = 0, Amount = 10000},
new Element{ ID = 1, Amount = 1000},
new Element{ ID = 2, Amount = 2000},
new Element{ ID = 3, Amount = 3000}
};
var numSmall = elements.Count();
var numBig = elements.LongCount();
var numSmallFiltered = elements. Count(e => e.Amount >= 2000);
var numBigFiltered = elements.LongCount(e => e.Amount >= 2000);
Console.WriteLine($"{nameof(numSmallFiltered)} type: {numSmallFiltered.GetType().Name}");
Console.WriteLine($"{nameof(numBigFiltered)} type: {numBigFiltered.GetType() .Name}");
Console.Read();
}
控制台结果
结果相同,只是更改了 datatype
。
LongCont
仅应用于非常大的集合。
Sum
Sum
扩展方法返回元素序列的 sum
。这些元素可以为 null
。
Sum
运算符有 20 个重载,分为 2 组。
第一组由只有单个参数的方法组成。此参数始终是数字,并且与源集合类型和返回类型相同。
签名组 1
public static long Sum(this IEnumerable<long> source);
public static long? Sum(this IEnumerable<long?> source);
public static float? Sum(this IEnumerable<float?> source);
public static double Sum(this IEnumerable<double> source);
public static double? Sum(this IEnumerable<double?> source);
public static decimal? Sum(this IEnumerable<decimal?> source);
public static decimal Sum(this IEnumerable<decimal> source);
public static float Sum(this IEnumerable<float> source);
public static int? Sum(this IEnumerable<int?> source);
public static int Sum(this IEnumerable<int> source);
示例
public static void SumTests1()
{
var elements = new List<Element>()
{
new Element{ ID = 0, Amount = 10000},
new Element{ ID = 1, Amount = 1000},
new Element{ ID = 2, Amount = 2000},
new Element{ ID = 3, Amount = 3000}
};
int[] numbers = new int[] { 1000, 2000, 3000 };
int resultNumbers = numbers.Sum();
var sumAmount = elements.Select(a => a.Amount).Sum();
Console.WriteLine($"{nameof(resultNumbers)} = {resultNumbers}");
Console.WriteLine($"{nameof(sumAmount)} = {sumAmount}");
Console.Read();
}
控制台结果
这些调用不包含参数,因为它是一个扩展方法参数。
第二组也有 10 个方法,在这种情况下,它们是带有两个参数的泛型方法。第一个参数与第一组相同。第二个 selector
参数等于 Select
扩展方法。
签名组 2
public static float? Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, float?> selector);
public static double? Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, double?> selector);
public static decimal Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, decimal> selector);
public static decimal? Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, decimal?> selector);
public static double Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, double> selector);
public static int? Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, int?> selector);
public static long? Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, long?> selector);
public static float Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, float> selector);
public static long Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, long> selector);
public static int Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, int> selector);
示例
public static void SumTests2()
{
var elements = new List<Element>()
{
new Element { ID = 0, Amount = 10000},
new Element { ID = 1, Amount = 1000},
new Element { ID = 2, Amount = 2000},
new Element { ID = 3, Amount = 3000}
};
var sumAmount = elements.Sum(a => a.Amount);
Console.WriteLine($"{nameof(sumAmount)} = {sumAmount}");
Console.Read();
}
我们添加一个 Func<Element, int>
参数。
控制台结果
对于 null
值,该过程将理解为,null = 0
。
public static void SumTestsNull()
{
var elements = new List<Element>()
{
new Element { ID = 0, Amount = null},
new Element { ID = 1, Amount = null},
new Element { ID = 2, Amount = null},
new Element { ID = 3, Amount = null}
};
var sumAmount = elements.Sum(a => a.Amount);
Console.WriteLine($"{nameof(sumAmount)} = {sumAmount}");
Console.Read();
}
控制台结果
Min 和 Max
Min
和 Max
运算符与 Sum
运算符具有相同的特性,但目标不同。Max
获取序列中的最大值,而 Min
获取最小值。
签名
public static double? Max(this IEnumerable<double?> source);
public static double Max(this IEnumerable<double> source);
public static long? Max(this IEnumerable<long?> source);
public static long Max(this IEnumerable<long> source);
public static int? Max(this IEnumerable<int?> source);
public static float Max(this IEnumerable<float> source);
public static float? Max(this IEnumerable<float?> source);
public static int Max(this IEnumerable<int> source);
public decimal Max(this IEnumerable<decimal> source);
public decimal? Max(this IEnumerable<decimal?> source);
public static long? Min(this IEnumerable<long?> source);
public static int? Min(this IEnumerable<int?> source);
public static int Min(this IEnumerable<int> source);
public static float? Min(this IEnumerable<float?> source);
public static double Min(this IEnumerable<double> source);
public static double? Min(this IEnumerable<double?> source);
public static decimal Min(this IEnumerable<decimal> source);
public static decimal? Min(this IEnumerable<decimal?> source);
public static long Min(this IEnumerable<long> source);
public static float Min(this IEnumerable<float> source);
public static int Max<TSource>(this IEnumerable<TSource> source,
Func<TSource, int> selector);
public static int? Max<TSource>(this IEnumerable<TSource> source,
Func<TSource, int?> selector);
public static long Max<TSource>(this IEnumerable<TSource> source,
Func<TSource, long> selector);
public static long? Max<TSource>(this IEnumerable<TSource> source,
Func<TSource, long?> selector);
public static float Max<TSource>(this IEnumerable<TSource> source,
Func<TSource, float> selector);
public static float? Max<TSource>(this IEnumerable<TSource> source,
Func<TSource, float?> selector);
public static double Max<TSource>(this IEnumerable<TSource> source,
Func<TSource, double> selector);
public static double? Max<TSource>(this IEnumerable<TSource> source,
Func<TSource, double?> selector);
public static decimal Max<TSource>(this IEnumerable<TSource> source,
Func<TSource, decimal> selector);
public static decimal? Max<TSource>(this IEnumerable<TSource> source,
Func<TSource, decimal?> selector);
public static long? Min<TSource>(this IEnumerable<TSource> source,
Func<TSource, long?> selector);
public static decimal? Min<TSource>(this IEnumerable<TSource> source,
Func<TSource, decimal?> selector);
public static decimal Min<TSource>(this IEnumerable<TSource> source,
Func<TSource, decimal> selector);
public static float Min<TSource>(this IEnumerable<TSource> source,
Func<TSource, float> selector);
public static double? Min<TSource>(this IEnumerable<TSource> source,
Func<TSource, double?> selector);
public static int? Min<TSource>(this IEnumerable<TSource> source,
Func<TSource, int?> selector);
public static int Min<TSource>(this IEnumerable<TSource> source,
Func<TSource, int> selector);
public static float? Min<TSource>(this IEnumerable<TSource> source,
Func<TSource, float?> selector);
public static long Min<TSource>(this IEnumerable<TSource> source,
Func<TSource, long> selector);
public static double Min<TSource>(this IEnumerable<TSource> source,
Func<TSource, double> selector);
简单示例
public static void MinMaxTests()
{
int[] numbers = new int[] { 1000, 2000, 3000 };
int maxNumbers = numbers.Max();
int minNumbers = numbers.Min();
Console.WriteLine($"{nameof(maxNumbers) } = {maxNumbers }");
Console.WriteLine($"{nameof(minNumbers) } = {minNumbers }");
Console.Read();
}
结果
复杂示例
public static void MinMaxTests2()
{
var elements = new List<Element>()
{
new Element { ID = 0, Amount = 10000},
new Element { ID = 1, Amount = 1000},
new Element { ID = 2, Amount = 2000},
new Element { ID = 3, Amount = 3000}
};
var maxAmountForSelect = elements.Max(a => a.Amount);
var minAmountForSelect = elements.Min(a => a.Amount);
var maxAmountForField = elements.Max(a => a.Amount);
var minAmountForField = elements.Min(a => a.Amount);
Console.WriteLine($"{nameof(maxAmountForSelect)} = {maxAmountForSelect}");
Console.WriteLine($"{nameof(minAmountForSelect)} = {minAmountForSelect}");
Console.WriteLine($"{nameof(maxAmountForField) } = {maxAmountForField }");
Console.WriteLine($"{nameof(minAmountForField) } = {minAmountForField }");
Console.Read();
}
结果
Average
与他的兄弟们(Sum
, Max
和 Min
)相同的公式、行为和处理,但用于查找平均值。
在这种情况下,返回类型始终是带有小数部分的类型,如 decimal 或 double。
签名
public static float Average(this IEnumerable<float> source);
public static double? Average(this IEnumerable<long?> source);
public static float? Average(this IEnumerable<float?> source);
public static double Average(this IEnumerable<double> source);
public static double Average(this IEnumerable<int> source);
public static decimal Average(this IEnumerable<decimal> source);
public static decimal? Average(this IEnumerable<decimal?> source);
public static double Average(this IEnumerable<long> source);
public static double? Average(this IEnumerable<double?> source);
public static double? Average(this IEnumerable<int?> source);
public static decimal Average<TSource>(this IEnumerable<TSource> source,
Func<TSource, decimal> selector);
public static decimal? Average<TSource>(this IEnumerable<TSource> source,
Func<TSource, decimal?> selector);
public static double? Average<TSource>(this IEnumerable<TSource> source,
Func<TSource, int?> selector);
public static double Average<TSource>(this IEnumerable<TSource> source,
Func<TSource, long> selector);
public static double? Average<TSource>(this IEnumerable<TSource> source,
Func<TSource, long?> selector);
public static float Average<TSource>(this IEnumerable<TSource> source,
Func<TSource, float> selector);
public static float? Average<TSource>(this IEnumerable<TSource> source,
Func<TSource, float?> selector);
public static double Average<TSource>(this IEnumerable<TSource> source,
Func<TSource, double> selector);
public static double? Average<TSource>(this IEnumerable<TSource> source,
Func<TSource, double?> selector);
public static double Average<TSource>(this IEnumerable<TSource> source,
Func<TSource, int> selector);
简单示例
public static void Average1()
{
int[] numbers = new int[] { 1000, 2000, 3000 };
var mediaNumbers = numbers.Average();
Console.WriteLine($"{nameof(mediaNumbers) } = {mediaNumbers }");
Console.Read();
}
结果
复杂示例
public static void Average2()
{
var elements = new List<Element>()
{
new Element { ID = 0, Amount = 10000},
new Element { ID = 1, Amount = 1000},
new Element { ID = 2, Amount = 2000},
new Element { ID = 3, Amount = 3000}
};
var mediaForSelect = elements.Max(a => a.Amount);
var mediaForField = elements.Max(a => a.Amount);
Console.WriteLine($"{nameof(mediaForSelect)} = {mediaForSelect}");
Console.WriteLine($"{nameof(mediaForField) } = {mediaForField}");
Console.Read();
}
结果
Aggregate
简而言之,扩展方法 Aggregate
用于累加函数。
Aggregate
有三个签名
public static TSource Aggregate<TSource>
(this IEnumerable<TSource> source, Func<TSource, TSource, TSource> func);
public static TAccumulate Aggregate<TSource, TAccumulate>
(this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func);
public static TResult Aggregate<TSource, TAccumulate, TResult>
(this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate,
TSource, TAccumulate> func, Func<TAccumulate, TResult> resultSelector);
第一个签名
这是最简单的签名。Aggregate
运算符调用委托匿名 Func<TAccumulate, TSource, TAccumulate> func
(累加函数),对集合中的每个元素执行。
public static void Aggregate1()
{
string[] names = { "Mike", "Julie", "John", "Laura", "Other name" };
string result = names.Aggregate((resultAcum, next) =>
resultAcum += string.Format("\r\n{0:-16}", next));
/// This is the translate in simple bucle
//string result = string.Empty;
//foreach (var next in nombres)
//{
// result += string.Format("\r\n{0:-16}", next);
//}
Console.WriteLine(result);
Console.Read();
}
结果
第二个签名
第二个签名与第一个签名相同,但第二个签名有一个新的 TSource
类型的参数。此参数显示初始累加值。
示例
public static void Aggregate2()
{
string[] names = { "Mike", "Julie", "John", "Laura", "Other name" };
string result = names.Aggregate("Inital Date",
(resultAcum, next) => resultAcum += string.Format("\r\n{0:-16}", next));
/// This is the translate in simple bucle
//string result = "Initial Date";
//foreach (var next in nombres)
//{
// result += string.Format("\r\n{0:-16}", next);
//}
Console.WriteLine(result);
Console.Read();
}
结果
第三个签名
最后一个签名具有与前一个签名相同的特征,但添加了一个新参数,用于配置输出格式。
简单示例
public static void Aggregate3()
{
string[] names = { "Mike", "Julie", "John", "Laura", "Other name" };
string result = names.Aggregate("Initial Date",
(resultAcum, next) => resultAcum += string.Format("\r\n{0:-16}", next));
string resultado = names.Aggregate("Inital Date",
(resultAcum, next) => resultAcum += string.Format("\r\n{0:-16}", next),
a => $"{a} \r\n --> Total characters: {a.Length} "
);
Console.WriteLine(resultado);
Console.Read();
}
结果
复杂示例
public static void Aggregate4()
{
string[] names = { "Mike", "Julie", "John", "Laura", "Other name" };
var separador = new string[] { "\r\n" };
var result = names.Aggregate("Inital Date",
(resultAcum, next) => resultAcum += string.Format("\r\n{0:-16}", next),
// We create an Anonymous type foreach element
a => a.Split(separador, StringSplitOptions.None).Select
(b => new { Dato = b, Len = b.Length })
);
result.ToList().ForEach(r => Console.WriteLine(r));
Console.Read();
}
结果
看到最后一个参数的目标非常重要。在此示例中,我们将名称字符串拆分,用于创建一个具有两个属性的新匿名对象:Dato
= (string name) 和 Len
= (dato 的长度)。
仅使用 Aggregate 重新创建所有运算符
现在我们将演示 Aggregate
是最重要的运算符,并且我们将使用此运算符创建所有先前的运算符。
Sum
public static void Sum_Aggregate()
{
int[] numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var sum = numbers.Aggregate((total, next) => total += next);
Console.WriteLine $"The {nameof(sum)} value is {sum}");
Console.Read();
}
结果。
最小值
public static void Min_Aggregate()
{
int[] numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var min = numbers.Aggregate((total, next) => next < total ? next : total);
Console.WriteLine($"The {nameof(min)} value is {min}");
Console.Read();
}
结果
最大值
public static void Max_Aggregate()
{
int[] numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var max = numbers.Aggregate((total, next) => next > total ? next : total);
Console.WriteLine($"The {nameof(max)} value is {max}");
Console.Read();
}
结果
Average
public static void Average_Aggregate()
{
int[] numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var average = numbers.Aggregate(
0,
(total, next) => total += next,
a => decimal.Parse(a.ToString()) /
decimal.Parse(numbers.Count().ToString())
);
Console.WriteLine($"The {nameof(average)} value is {average}");
Console.Read();
}
结果