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