如何实现类中数值属性的通用求和,无需随属性更新修改代码?
通用实现类集合数值属性求和,无需手动维护求和逻辑
我有一个Example类,需要对其集合执行求和操作,返回一个新的Example实例,其中每个数值属性的值为集合中对应属性的总和。目前手动编写的Sum扩展方法在新增类属性时必须同步修改,否则会出错,希望找到一种通用实现,新增数值属性时无需修改求和方法。
原代码如下:
public class Example { public Example() { } public Example(int a, int b, float c) { A = a; B = b; C = c; IgnoreThis = "Non numerical properties should be ignored by the sum method"; } public int A { get; set; } public int B { get; set; } public float C { get; set; } public string IgnoreThis {get; set;} }
using System.Collections.Generic; using System.Linq; public static class Extension { public static Example Sum(this IEnumerable<Example> source) { Example result = new Example(); result.A = source.Sum(x => x.A); result.B = source.Sum(x => x.B); result.C = source.Sum(x => x.C); return result; } }
using System.Collections.Generic; public class Usage { private List<Example> Examples = new List<Example>(); public void DoSomeLogic() { Examples.Add(new Example(1, 2, 3.75f)); Examples.Add(new Example(2, 3, 6.25f)); Example SumOfEachIndividualProperty = Examples.Sum(); //Expected result from logging SumOfEachIndividualProperty.A is 3; //Expected result from logging SumOfEachIndividualProperty.B is 5; //Expected result from logging SumOfEachIndividualProperty.C is 10; } }
解决方案:基于反射的通用求和实现
可以利用反射自动识别类中的数值属性并完成求和逻辑,新增数值属性时无需修改求和方法。
修改后的通用扩展方法
using System; using System.Collections.Generic; using System.Linq; using System.Reflection; public static class Extension { public static Example Sum(this IEnumerable<Example> source) { if (source == null) throw new ArgumentNullException(nameof(source)); var result = new Example(); var type = typeof(Example); // 筛选出可读写的公共数值属性(排除非数值类型) var numericProperties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(p => IsNumericType(p.PropertyType) && p.CanRead && p.CanWrite); foreach (var prop in numericProperties) { // 动态计算属性总和,兼容不同数值类型 var sum = source.Aggregate(Activator.CreateInstance(prop.PropertyType) as dynamic, (accumulator, item) => accumulator + prop.GetValue(item) as dynamic); // 将总和赋值给结果实例的对应属性 prop.SetValue(result, sum); } return result; } // 判断是否为有效数值类型(排除bool等非计算用类型) private static bool IsNumericType(Type type) { // 处理可空数值类型 type = Nullable.GetUnderlyingType(type) ?? type; return type == typeof(int) || type == typeof(float) || type == typeof(double) || type == typeof(long) || type == typeof(decimal) || type == typeof(short) || type == typeof(byte) || type == typeof(uint) || type == typeof(ulong) || type == typeof(ushort) || type == typeof(sbyte); } }
核心说明
- 自动识别属性:通过反射获取
Example类的所有公共实例属性,自动筛选出符合条件的数值属性,无需手动指定。 - 动态类型兼容:使用
dynamic处理不同数值类型的加法操作,避免针对每种类型编写重复求和逻辑。 - 无侵入扩展:新增数值属性(如
public int D { get; set; })时,无需修改Sum方法,反射会自动识别新属性并参与求和。
性能优化(可选)
如果需要高频调用求和方法,可以缓存数值属性信息,避免每次反射的开销:
public static class Extension { // 缓存数值属性,仅初始化一次 private static readonly PropertyInfo[] _cachedNumericProperties = typeof(Example) .GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(p => IsNumericType(p.PropertyType) && p.CanRead && p.CanWrite) .ToArray(); public static Example Sum(this IEnumerable<Example> source) { if (source == null) throw new ArgumentNullException(nameof(source)); var result = new Example(); foreach (var prop in _cachedNumericProperties) { var sum = source.Aggregate(Activator.CreateInstance(prop.PropertyType) as dynamic, (accumulator, item) => accumulator + prop.GetValue(item) as dynamic); prop.SetValue(result, sum); } return result; } // IsNumericType方法不变 private static bool IsNumericType(Type type) { type = Nullable.GetUnderlyingType(type) ?? type; return type == typeof(int) || type == typeof(float) || type == typeof(double) || type == typeof(long) || type == typeof(decimal) || type == typeof(short) || type == typeof(byte) || type == typeof(uint) || type == typeof(ulong) || type == typeof(ushort) || type == typeof(sbyte); } }
内容的提问来源于stack exchange,提问作者MattFace
相关产品推荐
相关产品推荐

