是否有一种更清晰的方法来单独总结类中的所有数值属性?

Mat*_*ace 3 c# linq

我有一个类,如下所示示例类。在我的 DoSomeLogic 方法中,我需要对示例类的集合求和,并希望返回示例类的新实例,其中每个数字属性都包含该集合中该属性的总和。

下面是我通常如何执行此操作的示例,但我的问题是我不断更新示例类,添加新属性等。执行此操作时,我需要记住也更新示例类的 sum 方法,或者我的代码已损坏,这似乎是我想避免的障碍/漏洞。

任何人都可以向我展示一种更好的方法来执行此操作,这样如果我向示例类添加新的数字属性,我就不需要更新 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;}
}
Run Code Online (Sandbox Code Playgroud)
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;
    }
}
Run Code Online (Sandbox Code Playgroud)
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;
    }
}
Run Code Online (Sandbox Code Playgroud)

Mat*_*son 5

您可以使用反射来解决这个问题,但我认为这太过分了(并且性能不会很好)。

您可以考虑将“添加”功能放入类本身中:

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 void Add(Example other)
    {
        A += other.A;
        B += other.B;
        C += other.C;
    }

    public int    A          { get; set; }
    public int    B          { get; set; }
    public float  C          { get; set; }
    public string IgnoreThis { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

那么你的扩展方法将变成:

public static Example Sum(this IEnumerable<Example> source)
{
    var result = new Example();

    foreach (var example in source)
    {
        result.Add(example);
    }

    return result;
}
Run Code Online (Sandbox Code Playgroud)

当您向类添加新成员时,Example您需要Add()相应地更新方法,但不需要修改扩展方法。


附录:由于有人想知道INumber在使用反射的实现中使用,我想我应该尝试一下。

首先,这是一个您想要对其属性求和的示例类:

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 override string ToString()
    {
        return $"A={A}, B={B}, C={C}";
    }

    public int    A { get; set; }
    public int    B { get; set; }
    public float  C { get; set; }
    public string IgnoreThis { get; set; } = "";
}
Run Code Online (Sandbox Code Playgroud)

下面是一个示例程序,演示了我们如何添加集合中所有项目的属性。请注意预期输出:

public static class Program
{
    public static void Main()
    {
        var items = new[]
        {
            new Example(1, 4, 7),
            new Example(2, 5, 8),
            new Example(3, 6, 9)
        };

        var totals = Extension.SumNumericProperties(items);

        Console.WriteLine(totals); // A=6, B=15, C=24
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我实现该类的方法Extension

如果您需要添加其他数字类型(例如decimal),您只需添加numericProperties具有所需类型的新项目并添加对sumValueTo()和 的调用assignResults()

public static class Extension
{
    public static T SumNumericProperties<T>(IEnumerable<T> items) where T: new()
    {
        var intAdders    = numericProperties<int,    T>();
        var floatAdders  = numericProperties<float,  T>();
        var shortAdders  = numericProperties<short,  T>();
        var doubleAdders = numericProperties<double, T>();

        foreach (var item in items)
        {
            sumValueTo(intAdders,    item);
            sumValueTo(floatAdders,  item);
            sumValueTo(shortAdders,  item);
            sumValueTo(doubleAdders, item);
        }

        T result = new();

        assignResults(intAdders,    result);
        assignResults(floatAdders,  result);
        assignResults(shortAdders,  result);
        assignResults(doubleAdders, result);

        return result;
    }

    static void sumValueTo<TNumber, T>(List<NumericPropertyAdder<TNumber>> numericProperties, T item) where TNumber : INumber<TNumber>
    {
        foreach (var numericProperty in numericProperties)
        {
            numericProperty.Add(item!);
        }
    }

    static void assignResults<TNumber, T>(List<NumericPropertyAdder<TNumber>> numericProperties, T item) where TNumber : INumber<TNumber>
    {
        foreach (var numericProperty in numericProperties)
        {
            numericProperty.AssignResult(item!);
        }
    }

    static List<NumericPropertyAdder<TNumber>> numericProperties<TNumber, TOwner>() where TNumber : INumber<TNumber>
    {
        return (
            from   prop in typeof(TOwner).GetProperties()
            where  prop.PropertyType.IsAssignableTo(typeof(INumber<TNumber>))
            select new NumericPropertyAdder<TNumber>(prop)
        ).ToList();
    }
}

public sealed class NumericPropertyAdder<T> where T: INumber<T>             
{
    public NumericPropertyAdder(PropertyInfo property)
    {
        _property = property;
    }

    public void Add(object propertyHolder)
    {
        var value = (T) _property.GetValue(propertyHolder)!;
        _sum += value;
    }

    public T Sum()
    {
        return _sum;
    }

    public void AssignResult(object propertyHolder)
    {
        _property.SetValue(propertyHolder, _sum);
    }

    T _sum = T.AdditiveIdentity;

    readonly PropertyInfo _property;
}
Run Code Online (Sandbox Code Playgroud)

我的结论是,处理这样的泛型类型所增加的复杂性实际上可能不值得......;)