C# 特性(Attributes)是用于在运行时为程序元素(如类、方法、属性等)添加声明性信息的一种方式。这些信息可以在程序运行时通过反射(Reflection)访问。特性可以用来控制程序行为、添加元数据或者影响程序的运行时行为。
.Net 框架提供了两种类型的特性:预定义特性和自定义特性。
用法如下:
#define DEBUG_PRINTusing System;
using System.Diagnostics;class Test
{// 如果定义的 DEBUG_PRINT ,该函数可以执行;没有定义DEBUG_PRINT,该函数将无法执行[Conditional("DEBUG_PRINT")]static void function1(){Console.WriteLine("DEBUG_PRINT In Function 1.");}[Obsolete("Don't use OldMethod, use NewMethod instead", true)]public static void OldMethod(){Console.WriteLine("It is the old method");}public static void NewMethod(){Console.WriteLine("It is the new method");}public static void Main(){Console.WriteLine("Main");//OldMethod(); 提示,Don't use OldMethod, use NewMethod insteadfunction1();}
}
Conditional
Conditional类似于条件编译,在上面的程序中当预定义DEBUG_PRINT,那么运行时就会调用function1,预定义放到了代码文件的最上面:
#define DEBUG_PRINT
Obsolete
Obsolete表示舍弃,可用于警告提示。
自定义属性
MyCustomAttribute.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace Attribute02
{[AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Struct|System.AttributeTargets.Method)]internal class MyCustomAttribute : System.Attribute{private string description;private string returnType;// 在构造函数中指定属性信息public MyCustomAttribute(string desc, string returnType){this.description = desc;this.returnType = returnType;}public string GetDescription(){return this.description;}public string GetReturnType(){return this.returnType;}}
}
使用MyCustomAttribute修饰类MyClass和它的成员方法GetSum
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace Attribute02
{[MyCustomAttribute("This is a custom description", "Class No return")]internal class MyClass{[MyCustomAttribute("This is a custom description", "Method int return")]public int GetSum(int x, int y){return x + y;}}
}
Main测试
namespace Attribute02
{internal class Program{static void Main(string[] args){Console.WriteLine("测试自定义属性");// 反射出类的属性信息Type type = typeof(MyClass);object[] attributes = type.GetCustomAttributes(typeof(MyCustomAttribute), true);foreach (MyCustomAttribute attr in attributes){Console.WriteLine(attr.GetDescription());Console.WriteLine(attr.GetReturnType());}// 反射出方法的属性信息var method = typeof(MyClass).GetMethod("GetSum");var attributesMethon = method.GetCustomAttributes(typeof(MyCustomAttribute), false);if (attributesMethon.Length > 0){var myAttribute = (MyCustomAttribute)attributesMethon[0];Console.WriteLine(myAttribute.GetDescription());Console.WriteLine(myAttribute.GetReturnType());}}}
}
运行结果如下:
测试自定义属性
This is a custom description
Class No return
This is a custom description
Method int return
自定义特性应用多个属性
在C#中,AttributeUsage 属性用来定义一个自定义属性可以应用到的程序元素,以及该属性是否可以多次应用和是否可以被继承。
validOn它是枚举器 AttributeTargets 的值的组合。默认值是 AttributeTargets.All。用以指定特性可以应用到哪些程序元素(如类、结构、方法等)上。
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]
AllowMultiple 参数是一个布尔值,指示是否可以对单一程序元素应用多个此类特性实例。
Inherited 参数也是一个布尔值,指示特性是否可以被派生类继承。
例如
namespace Attribute03
{// 定义一个自定义特性,可以应用于类和结构,可以被继承,并且允许多次应用[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = true, AllowMultiple = true)]public class MyCustomAttribute : Attribute{public string Name { get; set; }public MyCustomAttribute(string name){Name = name;}}
}
使用MyCustomAttribute修饰ExampleClass
namespace Attribute03
{// 应用自定义特性到一个类多次[MyCustomAttribute("ExampleClass")][MyCustomAttribute("AnotherNameForTheSameClass")]public class ExampleClass{// 类的实现内容}
}
Main测试
namespace Attribute03
{internal class Program{static void Main(string[] args){Console.WriteLine("Attribute03");// 通过反射获取特性信息var attributes = typeof(ExampleClass).GetCustomAttributes(typeof(MyCustomAttribute), true);foreach (MyCustomAttribute attr in attributes){Console.WriteLine($"Class {typeof(ExampleClass).Name} has the custom attribute with name: {attr.Name}");}}}
}
其它用法
C# 特性Attribute除了用于添加元数据和通过反射检索信息之外,它们还可用于以下目的:
-
控制程序行为:
- 编译器指令:特性可以用来给编译器提供指令,如
[Obsolete]
用于标记过时的代码元素。 - 条件编译:特性可用于条件编译,例如
[Conditional("DEBUG")]
可以使方法仅在 DEBUG 模式下编译和执行。
- 编译器指令:特性可以用来给编译器提供指令,如
-
数据验证:
- 在数据模型中,特性经常用于验证数据。例如,在实体框架(Entity Framework)或数据注释(Data Annotations)中,你可以使用
[Required]
,[StringLength]
等特性来定义数据验证规则。
- 在数据模型中,特性经常用于验证数据。例如,在实体框架(Entity Framework)或数据注释(Data Annotations)中,你可以使用
-
序列化和反序列化控制:
- 在数据序列化过程中,特性用于控制如何将对象转换为 XML 或 JSON 等格式。例如,
[Serializable]
、[DataContract]
和[DataMember]
。
- 在数据序列化过程中,特性用于控制如何将对象转换为 XML 或 JSON 等格式。例如,
-
拦截器和动态代理:
- 在面向切面编程(AOP)中,特性用于定义方法拦截器。这在动态代理创建时特别有用,例如在 .NET Core 中的依赖注入(DI)。
-
声明性安全:
- 特性可用于定义安全要求,如
[PrincipalPermission]
用于声明方法执行所需的安全上下文。
- 特性可用于定义安全要求,如
-
编写插件和扩展:
- 在插件架构中,特性可用于标识插件类或方法,便于动态加载和识别。
-
单元测试框架:
- 在单元测试中,特性用于标记测试方法和测试类(例如
[TestMethod]
或[TestClass]
),以及进行测试设置和清理(如[TestInitialize]
和[TestCleanup]
)。
- 在单元测试中,特性用于标记测试方法和测试类(例如
-
依赖注入配置:
- 在依赖注入(DI)中,特性可以用于标记构造函数、属性或方法,以指导 DI 容器如何进行注入。
-
框架和库集成:
- 许多框架和库使用特性来集成与特定框架或库的功能,如 ASP.NET Core 中的路由、授权和过滤器特性(如
[Route]
,[Authorize]
,[ActionFilter]
等)。
- 许多框架和库使用特性来集成与特定框架或库的功能,如 ASP.NET Core 中的路由、授权和过滤器特性(如
通过这些用途,C# 特性成为了一种强大的机制,可以在不改变代码本身逻辑的情况下丰富和扩展代码的功能。
代码如下:
1. 编译器指令([Obsolete]
特性)
public class MyClass
{[Obsolete("Use NewMethod instead", false)] // 标记为过时public void OldMethod(){Console.WriteLine("This is the old method.");}public void NewMethod(){Console.WriteLine("This is the new method.");}
}
2. 数据验证(使用数据注释)
using System.ComponentModel.DataAnnotations;public class User
{[Required]public string Name { get; set; }[StringLength(10, ErrorMessage = "ID cannot be longer than 10 characters.")]public string ID { get; set; }
}
3. 序列化和反序列化控制([Serializable]
和 [DataMember]
特性)
using System.Runtime.Serialization;[Serializable]
public class Person
{public string Name { get; set; }[DataMember]public int Age { get; set; }
}
4. 面向切面编程(AOP)中的方法拦截器
public class LoggingAttribute : Attribute
{// 这里只是示例,实际的拦截实现需要结合拦截器框架使用public void BeforeCall() => Console.WriteLine("Before method call");public void AfterCall() => Console.WriteLine("After method call");
}public class MyClass
{[Logging]public void MyMethod(){Console.WriteLine("Executing the method.");}
}
5. 声明性安全
using System.Security.Permissions;public class SecureClass
{[PrincipalPermission(SecurityAction.Demand, Role = "Administrator")]public void SecureMethod(){Console.WriteLine("This method requires the Administrator role.");}
}
6. 插件和扩展标识
public class PluginAttribute : Attribute
{public string Name { get; set; }
}[Plugin(Name = "MyPlugin")]
public class MyPlugin
{// 插件的实现
}
7. 单元测试
using Microsoft.VisualStudio.TestTools.UnitTesting;[TestClass]
public class MyTestClass
{[TestMethod]public void MyTestMethod(){// 测试代码}
}
8. 依赖注入配置
public class MyService
{[Inject]public IDependency MyDependency { get; set; }// 假设 Inject 是一个标记依赖注入的特性
}
9. 框架和库集成
using Microsoft.AspNetCore.Mvc;public class MyController : Controller
{[Route("api/myroute")]public IActionResult MyAction(){return Ok();}
}
以上代码示例涵盖了特性在 C# 中的各种不同用途,展示了特性如何被应用于实际编程场景中。