【问题标题】:Get max value and name from object properties从对象属性中获取最大值和名称
【发布时间】:2021-01-14 14:55:39
【问题描述】:

考虑到我有一个带有属性列表的对象

Class Indicators
{
    guid Id
    double? Indocator1;
    double? Indocator1;

    .
    .double? IndocatorP;
}

我想添加一个属性,直接告诉指标名称在所有指标属性中具有最大值。

有什么提示或建议吗?

【问题讨论】:

  • 如果属性是在类上定义的,它们的编号在编译时是已知的,不会改变。
  • 我有一个带有属性列表的对象 为什么不把数据放在一个列表中呢?
  • 您输入的是私有字段,而不是属性。你一开始想做什么?为什么使用带有编号的字段而不是列表的类?
  • 请提供有效的 C# 代码。还请与我们分享您到目前为止尝试了什么以及您在哪里卡住了。不要只是来这里寻求解决方案。

标签: c# .net reflection max


【解决方案1】:

您需要使用反射来获取属性列表并获取值列表,例如应用MaxLinq 方法:

public class Indicators
{
  public Guid Id { get; set; }
  public double? Indocator1 { get; set; }
  public double? Indocator2 { get; set; }
  public double? Indocator3 { get; set; }

  public (string name, double? value) GetMaxIndicator()
  {
    var pairs = GetType().GetProperties()
                         .Where(p => p.PropertyType == typeof(double?))
                         .Select(p => new
                                      {
                                        name = p.Name,
                                        value = (double?)p.GetValue(this)
                                      });
    string maxName= null;
    double? maxValue = null;
    foreach ( var item in pairs )
      if ( maxValue == null || item.value > maxValue )
      {
        maxName= item.name;
        maxValue = item.value;
      }
    return (maxName, maxValue );
  }
}

测试

static private void Test()
{
  var indicators = new Indicators();
  indicators.Indocator1 = 10;
  indicators.Indocator2 = null;
  indicators.Indocator3 = 20;
  var max = indicators.GetMaxIndicator();
  Console.WriteLine($"{max.name} = {max.value}");
}

输出

Indicator3 = 20

此示例假定成员是可读写的,如果出现重复,它会返回最后找到的成员,但您可以改为返回命名值元组的列表。

您可以将其用于带有GetFields 的字段。

您还可以将两者结合起来,如果您需要非公共或静态成员,您可以使用binding flags 过滤所需的成员。

改进以实现更简洁、更健壮的设计

如果指标数量固定,您应该更喜欢使用List 或数组。

实体类

public class Indicator : IComparable
{
  public string Name { get; set; }
  public double? Value { get; set; }
  public Indicator(string name, double? value = null)
  {
    Name = name;
    Value = value;
  }
  int IComparable.CompareTo(object obj)
  {
    var other = obj as Indicator;
    if ( other == null )
      throw new ArgumentException("Object is not an Indicator");
    if ( obj == null ) return 1;
    if ( Value == null && other.Value == null )
      return 0;
    else
    if ( Value == null )
      return -1;
    if ( other.Value == null )
      return 1;
    else
      return Value.Value.CompareTo(other.Value.Value);
  }
}

集合类

public class Indicators
{
  public Guid Id { get; set; }
  public List<Indicator> Items { get; } = new List<Indicator>();
  //or
  public Indicator[] Items { get; } = new Indicator[IndicatorsCount];
}

因此我们可以简单地写:

  var indicators = new Indicators();
  indicators.Items.Add(new Indicator("Indicator1", 10));
  indicators.Items.Add(new Indicator("Indicator2", null));
  indicators.Items.Add(new Indicator("Indicator3", 20));
  var max = indicators.Items.Max();
  Console.WriteLine($"{max.Name} = {max.Value}");

我们可以放入类本身:

public class Indicators
{
  public Guid Id { get; set; }
  public double?[] Items { get; } = new double?[IndicatorsCount];
  public double? MaxValue => Items.Max();
}

【讨论】:

  • 您可以使用 p => p.Name.StartsWith("Indocator") 作为在上述 GetProperties() 之后的 Where 子句中给出的谓词。否则,您的示例解释很棒。此外,您应该使用 GetFields,因为代码示例是字段而不是属性。而且由于没有给定访问器,因此无论如何都默认为“内部”,因此您需要 BindingFlags.Nonpublic | BindingFlags.Instance 最有可能。
  • 确实如此。我假设该类仅包含来自嵌入式设备的指标。由于标题,我放了一个使用属性的示例代码。
猜你喜欢
  • 1970-01-01
  • 2019-07-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-17
  • 2022-10-19
  • 2013-01-02
  • 1970-01-01
相关资源
最近更新 更多