asp.net core中如何自定义注解

翻译文章,原文

.NET(Core)中的自定义属性是一种有助于附加额外信息到类、结构甚至它们的成员的机制。在本文中,我们将通过一些实际示例来解释如何在.NET中创建、访问和获取自定义属性中的信息。

让我们开始。

声明自定义注解

我们可以通过创建一个类来定义一个属性。这个类应该继承自Attribute类。

Microsoft建议在类的名称末尾添加Attribute后缀。之后,我们派生类的每个属性将成为所需数据类型的参数。

自定义自定义属性的使用

AttributeUsageAttribute类通过定义一些基本特性来指定另一个属性类的使用方式。

This class has three members:

  • AttributeTargets enum
  • Inherited property (bool)
  • AllowMultiple property (bool)

AttributeTargets 枚举

AttributeTargets枚举指定了我们可以将自定义属性应用于的应用程序元素。

为了看看它是如何工作的,让我们创建一个名为TaskDescriptorAttribute的新类:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public class TaskDescriptorAttribute : Attribute
{
    public string? Name { get; set; }
    public string? Description { get; set; }
    public bool NeedsManager { get; set; }
    public int DeveloperCount { get; set; }
}

我们可以将这个属性(TaskDescriptorAttribute)只应用于类和结构,因为我们已经将它的目标设置为两者都使用了按位组合。当创建属性时,除了类和结构之外,我们还可以使用方法、枚举和其他应用程序元素。如果使用AttributeTargets.All值(它是默认值),这些属性也会应用于所有的应用程序元素。

让我们在MyTasks类中使用我们的属性:

[TaskDescriptor(Name = "The task's name",
    Description = "Some descriptions for the task",
    NeedsManager = true,
    DeveloperCount = 5)]
public class MyTasks
{
}

当我们将TaskDescriptorAttribute应用于一个类时,我们只使用TaskDescriptor部分,因为编译器允许我们在没有Attribute后缀的情况下使用它。

AllowMultiple属性

AllowMultiple属性允许多个实例的属性。这个属性可以是false(默认值)或true。

让我们创建另一个名为DeveloperTaskAttribute的属性:

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class DeveloperTaskAttribute : Attribute
{
    public Priorities Priority { get; set; }
    public string? Description { get; set; }
    public DeveloperTaskAttribute(Priorities priority)
    {
        Priority = priority;
    }
}

我们只能将这个属性应用于方法上,并且可以对它们应用多个实例。它有必需的Priority和可选的Description参数。

要应用这个属性,我们将在MyTasks类中创建一个新的ScheduleMeeting()方法:

public class MyTasks
{
    [DeveloperTask(Priorities.Low)]
    [DeveloperTask(Priorities.High, Description = "High level description")]
    public void ScheduleMeeting()
    {
    }
}

ScheduleMeeting()方法现在有两个DeveloperTask属性。我们使用第一个属性只声明了必需的参数,但是第二个属性声明了必需和可选参数。但是,我们不能定义一个没有Priorities参数的DeveloperTask属性,否则会得到编译器错误。

Inherited 属性

Inherited属性是我们可以应用于自定义属性的另一个关键特性。它指示该属性是否可以被继承。这个属性的默认值为true。

为了看到这个属性的用法,让我们创建ManagerTaskAttribute属性:

[AttributeUsage(AttributeTargets.Method, Inherited = false)]
public class ManagerTaskAttribute : Attribute
{
    public Priorities Priority { get; set; }
    public bool NeedsReport { get; set; }
}

这个新属性不能被继承,它的两个参数都是可选的。

现在,我们将在MyTasks类中创建ScheduleInterview方法来利用它:

public class MyTasks
{
    [ManagerTask(Priority = Priorities.Mid, NeedsReport = true)]
    [DeveloperTask(Priorities.High, Description = "High level description")]
    public virtual void ScheduleInterview()
    {
    }
}

这个方法有两个属性,一个DeveloperTask和一个ManagerTask属性。我们还添加了virtual关键字,因为我们想在另一个类中重写它。

所以,让我们创建一个继承自MyTasks类的YourTasks类:

public class YourTasks : MyTasks
{
    [DeveloperTask(Priorities.Mid, Description = "Mid level description")]
    public override void ScheduleInterview()
    {
    }
}

YourTasks类中的ScheduleInterview方法覆盖了基类MyTasks中的先前ScheduleInterview方法。这个方法没有ManagerTask属性,因为它的Inherited属性值为false。

但是,DeveloperTask属性的默认Inherited值为true。所以,YourTasks.ScheduleInterview方法有两个DeveloperTask属性。我们在YourTasks类内声明了第一个,而在MyTasks类内声明了第二个。

访问自定义属性的实例

一旦我们想要从属性中检索值,我们可以使用Attribute类的静态GetCustomAttribute方法。所以,让我们创建一个获取存储在TaskDescriptor实例中的信息的GetAttribute方法:

public static string? GetAttribute(Type desiredType, Type desiredAttribute)
{
    var attributeInstance = Attribute.GetCustomAttribute(desiredType, desiredAttribute);
    if (attributeInstance == null)
        Console.WriteLine($"The class {desiredType} does not have atributes.");
    else
        WriteOnTheConsole(attributeInstance);
    return attributeInstance?.ToString();
}

我们的GetAttribute方法接受一个类类型和一个属性类型作为输入参数,并在控制台上打印信息。

Attribute基类内部,我们可以找到GetCustomAttribute方法的不同重载。对于我们的示例,我们使用了GetCustomAttribute(MemberInfo element, Type attributeType)重载来获取我们想要的信息。

Type类继承自MemberInfo基类,所以我们可以将它作为第一个参数传递给方法。我们还将我们的自定义属性类型作为第二个参数发送。我们使用WriteOnTheConsole()方法内部的一些反射来在控制台上打印所有信息。(您可以查看源代码以获取实现

创建自定义属性的实例

GetCustomAttribute方法要么返回一个属性实例,要么返回一个null值。因此,如果存在,attributeInstance变量将存储我们自定义属性的一个实例。现在,我们将检索该实例的信息。

检索自定义属性的信息

为了检索我们自定义属性的信息,我们将调用GetAttribute方法,并将typeof(MyTasks)typeof(TaskDescriptorAttribute)作为其参数传递:

GetAttribute(typeof(MyTasks), typeof(TaskDescriptorAttribute));

如果GetAttribute方法找到了TaskDescriptorAttribute类的一个实例,我们应该将它的所有属性作为结果获取。

The CustomAttributes.TaskDescriptorAttribute attribute:
The Name property is: The task's name
The Description property is: Some descriptions for the task
The NeedsManager property is: True
The DeveloperCount property is: 5

我们成功地检索了自定义属性类的信息。

获取不同自定义属性的实例

有时,我们需要访问类成员的所有属性。Attribute基类有另一个GetCustomAttributes方法,可以将它们作为数组返回。

让我们创建一个GetAttributesOfMethods方法,以访问所有属性的实例并检索它们的信息:

public static List<string> GetAttributesOfMethods(Type elementType)
{
    List<string> attributes = new List<string>();
    var methodInfoList = elementType.GetMethods(BindingFlags.Public |
        BindingFlags.Instance |
        BindingFlags.DeclaredOnly);
    if (methodInfoList == null || methodInfoList.Length == 0)
    {
        Console.WriteLine($"The type {elementType} does not have any methods.");
        return attributes;
    }
    foreach (var methodInfo in methodInfoList)
    {
        var attributeList = Attribute.GetCustomAttributes(methodInfo, true);
        if (attributeList.Length == 0)
        {
            Console.WriteLine($"The {elementType.Name}.{methodInfo.Name} method does not have attributes.");
            continue;
        }
        Console.WriteLine($"The {elementType.Name}.{methodInfo.Name} method's attribute:");
        foreach (var att in attributeList)
        {
            WriteOnTheConsole(att);
            attributes.Add(methodInfo.Name + "-" + att.ToString());
        }
        Console.WriteLine();
    }
    return attributes;
}

我们想要获取每个已声明方法的所有属性。因此,我们使用适当的枚举调用GetMethods方法。

在第一个foreach循环内,我们使用GetCustomAttribute(MemberInfo element, bool inherit)重载调用GetCustomAttributes方法,并逐个发送已获取的MethodInfo值。我们还将true作为第二个参数传递,因为我们需要继承的属性。

在内部的foreach循环内,我们顺序获取attributeList数组的项目,并使用WriteOnTheConsole方法将它们打印到控制台上。

现在,让我们为MyTasks类调用GetAttributesOfMethods方法:

GetAttributesOfMethods(typeof(MyTasks));

我们将MyTasks类的类型发送给这个方法,作为结果,我们希望看到其方法的所有属性:

The MyTasks.ScheduleMeeting method's attribute:
The CustomAttributes.DeveloperTaskAttribute attribute:
The Description property is:
The Priority property is: Low
The CustomAttributes.DeveloperTaskAttribute attribute:
The Description property is: High level description
The Priority property is: High
The MyTasks.ScheduleInterview method's attribute:
The CustomAttributes.ManagerTaskAttribute attribute:
The Priority property is: Mid
The NeedsReport property is: True
The CustomAttributes.DeveloperTaskAttribute attribute:
The Description property is: High level description
The Priority property is: High

GetAttributesOfMethods打印了ScheduleMeeting和ScheduleInterview方法的属性。

我们也可以对YourTasks类执行相同的操作:

GetAttributesOfMethods(typeof(YourTasks));

并且发现结果非常相似:

The YourTasks.ScheduleInterview method's attribute:
The CustomAttributes.DeveloperTaskAttribute attribute:
The Description property is: Mid level description
The Priority property is: Mid
The CustomAttributes.DeveloperTaskAttribute attribute:
The Description property is: High level description
The Priority property is: High

我们在控制台上看不到ManagerTask属性,因为这个属性的Inherited值为false。我们看到两个DeveloperTask属性的原因是因为我们在MyTasks类内部声明了第一个属性,而在YourTasks类内部声明了第二个属性。

总结

在本文中,我们学习了如何在.NET中声明自定义属性。我们已经了解到我们可以将它们用于类及其成员。我们还触及了访问单个和多个属性实例的方式。最后,我们找出了如何检索它们的信息。

发表回复

您的电子邮箱地址不会被公开。