如何确定是否需要 属性?

How to determine if a property is required?

给定一个 class 这样的东西:

public class MyClass : ValidationValues
{
    public string Foo { get; set; }

    [Required(ErrorMessage = "Bar is required.")]
    public string Bar { get; set; }
    
    // and many more
}

public class ValidationValues
{
    public bool IsValid { get; set; } = true;
    public string InvalidReason { get; set; }
}

我需要确定是否需要 属性,同时将其作为通用列表进行循环。通过查看 Watch,我找到了一种方法,但感觉很笨拙,我认为它应该更简单。

对于某些上下文,此逻辑在 Azure 函数内部。所以没有视图,没有 MVC 等。该功能是一个 Blob 存储触发器,它选择一个带有 | 的 .CSV 文件。被反序列化为 List<MyClass> 的定界列表。我们不想在反序列化时强制执行 Required 属性,因为我们想要更精细的控制。

所以给定一个这样的文件:

value1 | |
value2 | something

最终返回给用户的是这样的:

[
    {
        "foo": "value1",
        "bar": "",
        "isValid": false,
        "InvalidReason" : "Bar is required"
    },
    {
        "foo": "value2",
        "bar": "something",
        "isValid": true,
        "InvalidReason" : ""
    }
]

这是我现在拥有的:

foreach (T item in itemList) // where 'itemList' is a List<T> and in this case T is MyClass
{
    foreach (PropertyInfo property in item.GetType().GetProperties())
    {
        if (property.CustomAttributes.ToList()[0].AttributeType.Name == "RequiredAttribute")
        {
             // validate, log, populate ValidationValues
        }
    }
}

这是我不喜欢的部分:

property.CustomAttributes.ToList()[0].AttributeType.Name == "RequiredAttribute"

有时当我想出一个编码挑战时,我告诉自己,“就是这样”。但在这种情况下,我很确定这不是方法。

反射很慢 - 或者至少相对较慢。所以;这里最重要的是:不要对每个 instance 执行此操作;您可以根据 Type(来自 GetType())缓存它,或者您可以只使用 T 而从不检查每个实例的 .GetType(),具体取决于您的意图。这包括缓存给定类型存在的属性,并且是必需的。但是,真正的奖励点是使用 meta-programming 发出 - 在运行时,或在 build-time 通过“生成器” - 一种方法*完全按照您的意愿行事,没有任何循环、测试等;即在这种情况下,它可能会发出一个方法,该方法相当于

void ValidateMyClass(MyClass obj)
{
    if (string.IsNullOrWhitespace(obj.Bar))
    {
        DoSomething("Bar is required.");
    }
}

这可以通过多种方式完成,包括 Expression API、发射 API (ILGenerator)、发射 C# 和使用 CSharpCodeProvider,或“生成器”API.

您可以使用 GetCustomAttibute:

重写该行
using System.Reflection;
foreach (T item in itemList) // where 'itemList' is a List<T> and in this case T is MyClass
{
    foreach (PropertyInfo property in item.GetType().GetProperties())
    {
        var attribute = property.GetCustomAttibute<RequiredAttribute>();
    }
}