【问题标题】:how to access the class properties based on condition如何根据条件访问类属性
【发布时间】:2021-12-01 11:17:44
【问题描述】:

我有下面的对象及其内部对象,并且我已将自定义属性添加到报告所需的属性名称中。

public class Space
{
    public SpaceIdentity SpaceIdentity { get; set; } = new();
    public SpaceGeometry SpaceGeometry { get; set; } = new();
    public AirBalance AirBalance { get; set; } = new();
    public EngineeringChecks EngineeringChecks { get; set; } = new();
}
public class SpaceIdentity
{
    public int ElementId { get; set; } // Not required
    [DisplayNameWithUnits(DisplayName = "Space Number", IsIncludedInReport2 = true)]
    public string Number { get; set; }
    [DisplayNameWithUnits(DisplayName = "Space Name", IsIncludedInReport2 = true, IsIncludedInReport1 = true)]
    public string Name { get; set; }
    [DisplayNameWithUnits(DisplayName = "Room Number", IsIncludedInReport1 = true)]
    public string RoomNumber { get; set; }
    [DisplayNameWithUnits(DisplayName = "Room Name", IsIncludedInReport1 = true)]
    public string RoomName { get; set; }
}

public class SpaceGeometry
{
    public Vertex LocationPoint { get; set; } // this is not required
    [DisplayNameWithUnits(DisplayName = "Space Area", Units = "(ft²)", IsIncludedInReport1 = true)]
    public double FloorArea { get; set; }
}
.....

在这里,我正在构建一个 Excel 报表,我想使用属性显示名称作为该报表的标题列名称。以下是多个报告中使用的一些属性属性信息。我所做的是我添加了一个布尔条件属性,如 (isIncludedInReport1) 并循环遍历 space 的属性并循环遍历内部对象的属性 (SpaceGeometry) 以获取特定的属性名称及其基于的属性值这个布尔条件。

我在这里寻找的是不添加这些布尔属性,有没有办法根据条件访问属性名称。我考虑过添加接口,但这里不可能,因为我有多个内部类,它们的属性需要包含在单个报告中。

谁能告诉我有没有其他方法可以做到这一点?

更新:

   var columnResult = new OrderedDictionary();
    GetReportHeaderColumnName(typeof(Space), columnResult);
    
    public static void GetReportHeaderColumnName(Type type, OrderedDictionary headerNameByUnit)
    {
        var properties = type.GetProperties();
        foreach (var propertyInfo in properties)
        {
            if (propertyInfo.PropertyType.IsClass && !propertyInfo.PropertyType.FullName.StartsWith("System."))
            {
                if (propertyInfo.PropertyType == typeof(Overridable<double>))
                {
                    AddReportHeaderName(headerNameByUnit, propertyInfo);
                }
                else
                {
                    GetReportHeaderColumnName(propertyInfo.PropertyType, headerNameByUnit);
                }
            }
            else
            {
                AddReportHeaderName(headerNameByUnit, propertyInfo);
            }
        }
    }

    protected static void AddReportHeaderName(OrderedDictionary columnResult, PropertyInfo propertyInfo)
    {
        if (propertyInfo.GetCustomAttributes(typeof(DisplayNameWithUnitsAttribute), true).Any())
        {
            var displayNameWithUnitsAttribute = (DisplayNameWithUnitsAttribute)Attribute.GetCustomAttribute(propertyInfo, typeof(DisplayNameWithUnitsAttribute));

            if (displayNameWithUnitsAttribute.IsIncludedInReport2)
            {
                columnResult.Add(displayNameWithUnitsAttribute.DisplayName, displayNameWithUnitsAttribute.Units);
            }
        }
    }

【问题讨论】:

  • 你能不能展示你到目前为止做了什么,有什么问题。添加任何属性很容易,但是在此之后您要做什么?你真的需要它们吗?
  • 我不想使用这些布尔属性来访问属性名称,目前我正在使用这些。
  • @Serge,我更新了我的问题
  • 听起来您在问我们如何在不使用属性的情况下指定要包含在报告中的列。那正确吗?如果是这样,我们需要了解用例。谁负责决定某个列是否包含在报告中?他们会喜欢如何表达他们的偏好?
  • @EnigmaState 这里的问题是您坚持通过说明系统应该做什么来指定需求。最好通过说明系统应该做什么来指定需求。例如,如果最终用户可以通过在 Excel 工作簿的单元格中键入列名来指定列名,您可能会喜欢它。或者,也许您要求它们只能由开发人员配置。你需要缩小范围。说它不应该使用属性告诉我们很少。可能有一百种方法可以做到。

标签: c# .net .net-core reflection properties


【解决方案1】:

另一种不使用反射的方法是只使用ReportProperty的列表

public record ReportProperty<T>(
    Func<T, string> ValueFunc,
    string DisplayName,
    string? Unit = null
    );

List<ReportProperty<Space>> report1 = new(){
    new( s => s.SpaceIdentity.Number, "Space Number"),
    new( s => s.SpaceIdentity.RoomNumber, "Room Number"),
    new( s => s.SpaceIdentity.RoomName, "Room Name"),
    new( s => s.SpaceGeometry.FloorArea.ToString(), "Floor Area", "Ft2"),
};


【讨论】:

  • 感谢您的意见。我仍然需要使用反射来获取显示名称属性和单位,但我不想使用布尔属性。我正在考虑制作一个接口并转换该接口以获取属性信息。界面是许多属性的组合,这就是我要退后的地方。我不想硬编码名称,因为一份报告中有超过 70 列。
  • 如果您不希望将属性名称作为代码中的属性,您想要在哪里呢?它应该在其他地方编码吗?是否应该在运行时从文本文件中加载?来自数据库?
  • 我将显示名称和属性名称保存在一个地方,以便如果有任何更改,它将是一个地方。如果我必须在另一个地方硬编码属性名称,那么跟踪将是至关重要的。
  • 你没有回答这个问题。您想要属性名称在哪里?您必须在某个地方列出它们。这需要是一个文本文件,还是应该在 c# 代码中的列表中?
  • 您的意思是每个报告所需的属性名称吗?我将这些模型放在另一个 c# 库中,并在主 API 中使用它们。如果我放置 bool 之类的属性,这会使共享库过于依赖其消费者(API)之一。同时,我不想将所需的属性名称写在单独的列表中,并将它们与实际的属性名称进行比较。
【解决方案2】:

您想通过另一种方式实现什么?我假设您想让您的代码可扩展,以便更轻松地添加更多报告。

您还想在类本身上使用属性吗?如果是这样,您可以创建一个新属性,并像这样标记您的属性:

public class SpaceIdentity
{
    public int ElementId { get; set; } // Not required
    [DisplayNameWithUnits(DisplayName = "Space Number")]
    [IncludeInReport(2)]
    public string Number { get; set; }
    
    [DisplayNameWithUnits(DisplayName = "Space Name")]
    [IncludeInReport(1)]
    [IncludeInReport(2)]
    public string Name { get; set; }

    [DisplayNameWithUnits(DisplayName = "Room Number")]
    [IncludeInReport(1)]
    public string RoomNumber { get; set; }

    [DisplayNameWithUnits(DisplayName = "Room Name")]
    [IncludeInReport(1)]
    public string RoomName { get; set; }
}

或者您想将报告与类分开,以便在另一个文件中定义您的报告?如果是这样,也许属性名称的二维列表可以解决问题:

List<List<string>> Report1 = new()
{
    new(){nameof(Space.SpaceIdentity), nameof(SpaceIdentity.Name)},
    new(){nameof(Space.SpaceIdentity), nameof(SpaceIdentity.RoomNumber)},
    new(){nameof(Space.SpaceIdentity), nameof(SpaceIdentity.RoomName)},
    new(){nameof(Space.SpaceGeometry), nameof(SpaceGeometry.FloorArea)}
};

【讨论】:

  • 我不想使用布尔属性来识别报告中包含哪些内容,并且不想对标题名称进行硬编码。以后会比较麻烦。我正在寻找任何其他方法来实现同样的目标。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-06
  • 2011-05-07
  • 2012-12-20
  • 2022-01-26
  • 1970-01-01
  • 2020-07-14
相关资源
最近更新 更多