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中声明自定义属性。我们已经了解到我们可以将它们用于类及其成员。我们还触及了访问单个和多个属性实例的方式。最后,我们找出了如何检索它们的信息。
发表回复