有没有一种更简洁的方法来逐个总结类中的所有数值属性?

huangapple go评论74阅读模式
英文:

Is there a cleaner way to sum all numerical properties in a class individually?

问题

我有一个类,如下所示,称为 Example class。在我的 DoSomeLogic 方法中,我需要对 Example 类的集合进行求和,并希望返回一个新的 Example 类的实例,其中每个数值属性包含来自集合的该属性的总和。

以下是我通常会这样做的示例,但我的问题是我不断更新我的示例类,添加新属性等。在这样做时,我需要记得同时更新示例类的 sum 方法,否则我的代码会出错,这似乎是我想要避免的障碍/漏洞。

有人可以向我展示更好的通用方法,这样如果我向示例类添加新的数值属性,我就不需要更新 sum 方法吗?

public class Example
{
    public Example()
    {
    }

    public Example(int a, int b, float c)
    {
        A = a;
        B = b;
        C = c;
        IgnoreThis = "sum 方法应该忽略非数值属性";
    }

    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();
        //从记录 SumOfEachIndividualProperty.A 应得到预期结果 3;
        //从记录 SumOfEachIndividualProperty.B 应得到预期结果 5;
        //从记录 SumOfEachIndividualProperty.C 应得到预期结果 10;
    }
}

希望这对你有帮助!

英文:

I have a class, represented below as Example class. In my DoSomeLogic Method, I need to sum a collection of Example class, and want to return a new instance of Example class where each numerical property contains the sum of that property from the collection.

Below is an example of how i would typically do this, but my problem is that I am continuously updating my example class, adding new properties etc. When doing this I need to remember to also update the sum method for example class too, or my code is broken, which seems like a hurdle/vulnerability I'd like to avoid.

Can anyone show me a better way to do this generically, so i don't need to update the sum method if i add a new numerical property to the example class?

public class Example 
{
    public Example()
    {
    }

    public Example(int a, int b, float c)
    {
        A = a;
        B = b;
        C = c;
        IgnoreThis = &quot;Non numerical properties should be ignored by the sum method&quot;;
    }

    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&lt;Example&gt; source)
    {
        Example result = new Example();
        result.A = source.Sum(x =&gt; x.A);
        result.B = source.Sum(x =&gt; x.B);
        result.C = source.Sum(x =&gt; x.C);
        return result;
    }
}
using System.Collections.Generic;

public class Usage
{
    private List&lt;Example&gt; Examples = new List&lt;Example&gt;();

    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;
    }
}

答案1

得分: 5

以下是您要的翻译部分:

You could solve this using reflection, but I think that's overkill (and would not be very performant).
您可以使用反射来解决这个问题,但我认为这有点过于复杂(而且性能可能不佳)。

You could consider putting the "add" functionality into the class itself:
您可以考虑将“add”功能放入类本身:

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; }

}

Then your extension method would become:
然后,您的扩展方法将变为:

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

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

return result;

}

ADDENDUM: Since someone was wondering about using INumber in the implementation that uses reflection, I thought I'd have a go.
附录:因为有人在想是否可以在使用反射的实现中使用INumber,我想试一试。

Firstly, here's a example class that you want to sum the properties for:
首先,这是一个示例类,您想要对其属性进行求和:

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; } = "";

}

Here's a sample program that demonstrates how we want to add up the properties for all the items in a collection. Note the expected output:
以下是一个演示如何对集合中的所有项目的属性进行求和的示例程序。请注意期望的输出:

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
}

}

And here's how I implemented the Extension class:
以下是我如何实现Extension类的方法:

If you need to add additional numeric types (e.g. decimal) you'd just need to add an new numericProperties item with the required type and add calls to sumValueTo() and assignResults():
如果您需要添加其他数值类型(例如decimal),只需添加一个具有所需类型的新numericProperties项目,并添加调用sumValueTo()和assignResults()。

public static class Extension
{
public static T SumNumericProperties(IEnumerable 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 where T: INumber
{
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;

}

My conclusion is that the added complexity of handling the generic types like this might not actually be worth it... 有没有一种更简洁的方法来逐个总结类中的所有数值属性?
我的结论是,处理这种泛型类型的额外复杂性可能实际上不值得。 有没有一种更简洁的方法来逐个总结类中的所有数值属性?

英文:

You could solve this using reflection, but I think that's overkill (and would not be very performant).

You could consider putting the "add" functionality into the class itself:

public class Example
{
    public Example()
    {
    }

    public Example(int a, int b, float c)
    {
        A = a;
        B = b;
        C = c;
        IgnoreThis = &quot;Non numerical properties should be ignored by the sum method&quot;;
    }

    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; }
}

Then your extension method would become:

public static Example Sum(this IEnumerable&lt;Example&gt; source)
{
    var result = new Example();

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

    return result;
}

When you add new members to the Example class you would need to update the Add() method accordingly, but you would not need to modify the extension method.


ADDENDUM: Since someone was wondering about using INumber in the implementation that uses reflection, I thought I'd have a go.

Firstly, here's a example class that you want to sum the properties for:

public class Example
{
    public Example()
    {
    }

    public Example(int a, int b, float c)
    {
        A = a;
        B = b;
        C = c;
        IgnoreThis = &quot;Non numerical properties should be ignored by the sum method&quot;;
    }

    public override string ToString()
    {
        return $&quot;A={A}, B={B}, C={C}&quot;;
    }

    public int    A { get; set; }
    public int    B { get; set; }
    public float  C { get; set; }
    public string IgnoreThis { get; set; } = &quot;&quot;;
}

Here's a sample program that demonstrates how we want to add up the properties for all the items in a collection. Note the expected output:

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
    }
}

And here's how I implemented the Extension class:

If you need to add additional numeric types (e.g. decimal) you'd just need to add an new numericProperties item with the required type and add calls to sumValueTo() and assignResults().

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

        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&lt;TNumber, T&gt;(List&lt;NumericPropertyAdder&lt;TNumber&gt;&gt; numericProperties, T item) where TNumber : INumber&lt;TNumber&gt;
    {
        foreach (var numericProperty in numericProperties)
        {
            numericProperty.Add(item!);
        }
    }

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

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

public sealed class NumericPropertyAdder&lt;T&gt; where T: INumber&lt;T&gt;             
{
    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;
}

My conclusion is that the added complexity of handling the generic types like this might not actually be worth it... 有没有一种更简洁的方法来逐个总结类中的所有数值属性?

答案2

得分: 2

以下是您要翻译的内容:

"Thanks to the fantastic answers provided by Vladim Martynov and Matthew Watson I decided to move the add function to the example class as suggested by MW.

I also decided to use reflection to validate the add function once on application startup (in my developer context only), to avoid the performance hit of using reflection in production code.

so my final code looks something like the below"

using System.Linq;
using System.Reflection;
public class Example 
{
    public Example()
    {
    }

    public Example(int a, int b, float c)
    {
        A = a;
        B = b;
        C = c;
    }

    public static bool ValidateAddMethod()
    {
        var intProperties = typeof(Example)
      .GetProperties()
      .Where(x => x.PropertyType == typeof(int));
        var floatProperties = typeof(Example)
        .GetProperties()
      .Where(x => x.PropertyType == typeof(float));
        Example example1 = new Example();

        foreach(PropertyInfo intInfo in intProperties)
        {
            intInfo.SetValue(example1, 1);
        }

        foreach(PropertyInfo floatInfo in floatProperties)
        {
            floatInfo.SetValue(example1, 1f);
        }

        Example example0 = new Example();

        example0.Add(example1);

        foreach (PropertyInfo intInfo in intProperties)
        {
            int currentInt = (int)intInfo.GetValue(example0);
            if (currentInt != 1)
                throw new System.Exception($"The property {intInfo.Name} has not been added to the sum method of the Example class.");
        }

        foreach (PropertyInfo floatInfo in floatProperties)
        {
            float currentFloat = (float)floatInfo.GetValue(example0);
            if (currentFloat != 1f)
                throw new System.Exception($"The property {floatInfo.Name} has not been added to the sum method of the Example class.");
        }
        return true;
    }

    public void Add(Example other)
    {
        this.A += other.A;
        this.B += other.B;
        this.C += other.C;
    }

    public int A { get; set; }
    public int B { get; set; }
    public float C { get; set; }
    public float NotAdded { get; set; }
}
using System.Collections.Generic;

public static class Extension
{
    public static Example Sum(this IEnumerable<Example> source)
    {
        Example result = new Example();
        foreach(Example example in source)
        {
            result.Add(example);
        }
        return result;
    }
}
using System.Collections.Generic;

public class Usage
{
    //Not my actual implementation but should serve to illustrate the idea.
    bool IsDeveloperMode = true;

    public Usage()
    {
        if(IsDeveloperMode)
        {
            Example.ValidateAddMethod();
        }

    }

    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 SumEachIndividualProperty = Examples.Sum();
    }
}
英文:

Thanks to the fantastic answers provided by Vladim Martynov and Matthew Watson I decided to move the add function to the example class as suggested by MW.

I also decided to use reflection to validate the add function once on application startup (in my developer context only), to avoid the performance hit of using reflection in production code.

so my final code looks something like the below

using System.Linq;
using System.Reflection;
public class Example 
{
    public Example()
    {
    }

    public Example(int a, int b, float c)
    {
        A = a;
        B = b;
        C = c;
    }

    public static bool ValidateAddMethod()
    {
        var intProperties = typeof(Example)
      .GetProperties()
      .Where(x =&gt; x.PropertyType == typeof(int));
        var floatProperties = typeof(Example)
        .GetProperties()
      .Where(x =&gt; x.PropertyType == typeof(float));
        Example example1 = new Example();

        foreach(PropertyInfo intInfo in intProperties)
        {
            intInfo.SetValue(example1, 1);
        }

        foreach(PropertyInfo floatInfo in floatProperties)
        {
            floatInfo.SetValue(example1, 1f);
        }

        Example example0 = new Example();

        example0.Add(example1);

        foreach (PropertyInfo intInfo in intProperties)
        {
            int currentInt = (int)intInfo.GetValue(example0);
            if (currentInt != 1)
                throw new System.Exception($&quot;The property {intInfo.Name} has not been added to the sum method of the Example class.&quot;);
        }

        foreach (PropertyInfo floatInfo in floatProperties)
        {
            float currentFloat = (float)floatInfo.GetValue(example0);
            if (currentFloat != 1f)
                throw new System.Exception($&quot;The property {floatInfo.Name} has not been added to the sum method of the Example class.&quot;);
        }
        return true;
    }

    public void Add(Example other)
    {
        this.A += other.A;
        this.B += other.B;
        this.C += other.C;
    }

    public int A { get; set; }
    public int B { get; set; }
    public float C { get; set; }
    public float NotAdded { get; set; }
}


using System.Collections.Generic;

public static class Extension
{
    public static Example Sum(this IEnumerable&lt;Example&gt; source)
    {
        Example result = new Example();
        foreach(Example example in source)
        {
            result.Add(example);
        }
        return result;
    }
}
using System.Collections.Generic;

public class Usage
{
    //Not my actual implementation but should serve to illustrate the idea.
    bool IsDeveloperMode = true;

    public Usage()
    {
        if(IsDeveloperMode)
        {
            Example.ValidateAddMethod();
        }

    }

    private List&lt;Example&gt; Examples = new List&lt;Example&gt;();

    public void DoSomeLogic()
    {
        Examples.Add(new Example(1, 2, 3.75f));
        Examples.Add(new Example(2, 3, 6.25f));
        Example SumEachIndividualProperty = Examples.Sum();
    }
}

huangapple
  • 本文由 发表于 2023年3月3日 20:36:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/75627160.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定