C#中的特性

什么是特性

来看一段微软官方文档的描述:

使用特性,可以有效地将元数据或声明性信息与代码(程序集、类型、方法、属性等)相关联。 将特性与程序实体相关联后,可以在运行时使用「反射」这项技术查询特性。

看完之后好像明白了什么,但又没完全明白。其实特性非常常见,我们直接通过实例来理解究竟什么是特性:

Serializable

首先,当一个类需要支持序列化时,我们总能看到类名上方有如下的结构:

[Serializable]
class SampleClass
{
	// ...
}

[Serializable]就是用来标识当前类是否可序列化的。

Obsolete

当我们的程序在迭代过程中出现了一些打算弃用的方法,如果直接删除的话,势必会影响到版本的兼容性。因此需要添加一个标识,让开发者注意到这个方法已经弃用,在未来的版本中可能会被删除。这就需要用到[Obsolete]特性:

class SampleClass
{
	// ...
	[Obsolete]
	public static void Test()
	{
		
	}
}

这样在调用被[Obsolete]标记的方法时,编译器就会进行警告:

此外,该特性还支持添加参数:

// 参数一:提示信息 参数二:是否error
[Obsolete("该方法已被弃用",true)]  
public static void Test()  
{  
    
}

Conditional

在开发环境下,我们经常需要编写一些调试方法,而在打包时又需要去除这些代码。挨个删除显然是不现实的,此时就需要用到[Conditional]特性。它需要在参数中传递一个字符串,编译器会根据这个字符串寻找同名的宏。如果这个宏定义了,该特性修饰的方法就会启用,反之则禁用。

#define IsShowMessage

// ...
[Conditional("IsShowMessage")]
public static void Test2()
{
	Console.WriteLine("调试信息。。。。");
}
// ...

调用者信息特性

利用Caller特性,可以将调用者的某些信息(如行号、文件路径、成员名称等)作为参数传入。不过需要注意用该特性修饰的参数需要具有默认值。

// ...
public static void Test3(String message,[CallerLineNumber] int lineNum=0
	,[CallerFilePath] string path="",[CallerMemberName] string name="")
{
	Console.WriteLine(message);
	Console.WriteLine(lineNum);
	Console.WriteLine(path);
	Console.WriteLine(name);
}
// ...
public static void AttributesPracticeMain()
{
	SampleClass.Test3("调用Test3");
}

输出结果如下:

DebuggerStepThrough

这个特性看名字就能明白个大概,就是被这个特性修饰的方法,在调试模式下不会进入。

[DebuggerStepThrough]
public static void Test4()
{
	Console.WriteLine("调试信息。。。。");
}

什么是特性

看完了上面的几个示例,我们再来理解特性的定义:

特性(attribute)是一种允许我们向程序的程序集添加元数据的语言结构。它是用于保存程序结构信息的某种特殊类型的类。

  • 将应用了特性的程序结构叫做目标
  • 设计用来获取和使用元数据的程序(对象浏览器)叫做特性的消费者
  • NET预定了很多特性,我们也可以声明自定义特性

也就是说,我们在编写的类、方法、参数中添加了特性,编译器就会根据特性产生元数据,再将这些元数据放入程序集中。消费者就可以获取到这些元数据,并进行使用。

自定义特性

如果还是不明白特性究竟是什么东西,我们就来自己实现一个特性,看看它究竟是如何运作的。

首先特性本质上就是一个类,我们自定义的特性类名中必须包含Attribute,同时继承Attribute类。然后需要在该类上通过AttributeUsage特性指明自定义特性的应用范围:

[AttributeUsage(AttributeTargets.Class)]
public sealed class ClassInfoAttribute:Attribute
{
	public string Author { get; }
	public string CreateDate { get; }
	public string Description { get; }

	public ClassInfoAttribute(string author, string createDate, string description)
	{
		Author = author;
		CreateDate = createDate;
		Description = description;
	}
}

然后就可以直接使用这个特性了。不过需要注意的一点是,使用自定义特性时不需要加Attribute后缀。通过反射可以获取到指定的特性:

[ClassInfo("张三","2022/08/13","这是一个自定义特性测试类")]
class AttributeTest
{
	public static void AttributeTestMain()
	{
		var type = typeof(AttributeTest);
		// 是否定义了ClassInfoAttribute特性
		if (type.IsDefined(typeof(ClassInfoAttribute), false))
		{
			var customAttributes = type.GetCustomAttributes(false);
			if (customAttributes is {Length: > 0})
			{
				var classInfo = Array.Find(customAttributes, item => item is ClassInfoAttribute) as ClassInfoAttribute;
				Console.WriteLine($"Author: {classInfo?.Author} Date: {classInfo?.CreateDate} Des: {classInfo?.Description}");
			}
		}
	}
}

输出信息如下:


参考文献:
[1]马克·米凯利斯.C#8.0本质论[M].机械工业出版社.

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值