C# 特性(Attributes)是用于在运行时为程序元素(如类、方法、属性等)添加声明性信息的一种方式。这些信息可以在程序运行时通过反射(Reflection)访问。特性可以用来控制程序行为、添加元数据或者影响程序的运行时行为。
.Net 框架提供了两种类型的特性:预定义特性和自定义特性。
用法如下:
#define DEBUG_PRINT
using 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 instead
function1();
}
}
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# 中的各种不同用途,展示了特性如何被应用于实际编程场景中。