C#里面有特性,属性呀,很多面试题都会问,特性和属性有啥区别,其实这两个东西没有啥关联,只是名字带有一个相同的字眼而已,稍微我会解释我为啥这么说,那么今天我们先来学学特性。
(一) 什么是特性
经常写代码的同学应该碰到过,这些特性[Serializable],[FormUri],[Obsolete],,,,特性给我们的感觉就是它貌似给我们的类或者方法,属性字段,加多了一层处理逻辑一样。其实我们的感觉没有错,特性就是在不破坏原来的结构上,封装了额外的操作处理或流程。想想平时碰到的,用特性来做日志处理,权限验证,AOP是不是这个道理。下面我用图的方式来告诉大家一些内容。
上图是对我们C#程序运行的一个简单介绍。
这幅图主要说明了特性经过编译器编译后是存在程序集的元数据里面的,如果你不对元数据进行操作,特性是不会起任何效果的。
(二)声明和使用Attribute
特性:其实就是一个类,一个直接继承或间接继承自Attibute类。这个类的命名呢,一般是以Attribute结尾,但是在使用的时候,我们可以不写后缀Attribute;
特性可以写在属性,方法,类,返回值。下面来看一个例子;
新建了一个叫User的类,里面有四项Id,Name,Email,Tel;
static void Main(string[] args) { var userList = new List<User>(); userList.Add(new User() {Id=1,Name ="zhangzhen",Email ="GDOUJKZZ@163.com",Tel ="110"}); userList.Add(new User() { Id = 2, Name = "zhangzhen2", Email = "GDOUJKZZ@163.com", Tel = "110" }); userList.Add(new User() { Id = 3, Name = "zhangzhen3", Email = "GDOUJKZZ@163.com", Tel = "110" }); foreach (var u in userList){ Console.WriteLine($"Id={u.Id},Name={u.Name}"); } }
现在控制台输出是:
忽然有一天需求来了说,我不想要Id,我想要编号,名称这种形式。那现在我就问问,大家应该怎么做呢,A说:那把Id和Name改成编号,名称这种形式了;B说:写入资源文件里面去;C说:用特性,得了,我们这里用特性来实现(A和B的请各位客观自己想想有啥不好的)。
那我们就用特性实现呗。
我们先定义了一个叫做ChineseNameAttribute,如下:
/// <summary> /// 自定义特性 /// </summary> public class ChineseNameAttribute:Attribute { private readonly string _chineseName; public ChineseNameAttribute(string chineseName) { this._chineseName = chineseName; } public string GetChineseName(){ return this._chineseName; } }
定义完了之后,啥也不说,马上给刚刚的User类加上,
public class User { [ChineseName("用户编号")] public int Id { get; set; } [ChineseName("用户名称")] public string Name { get; set; } [ChineseName("用户邮箱")] public string Email { get; set; } [ChineseName("用户电话")] public string Tel { get; set; } }
加好了,之后,我们马上来看看效果。
在运行刚刚那段代码,啥也没有,啥也没出来,我们不是添加了特性了吗...噢噢噢噢,明白了原来特性经过编译器编译之后是存在程序集的元数据的,我们知道元数据对对我们程序集的一些清单,描述信息。那我们应该要怎么让特性取作用了,其实这个问题就是我们如何让元数据来影响我们程序的运行呢---》这个就是典型的反射。
foreach (var u in userList) { var uType = u.GetType(); foreach (var item in uType.GetProperties()) { var attrArr = item.GetCustomAttributes(typeof(ChineseNameAttribute), false); //这里返回的是一个数组 var attrName = item.Name; if (attrArr.Length > 0) { var chineseNameAttr = attrArr[0] as ChineseNameAttribute; attrName = chineseNameAttr.GetChineseName(); } Console.WriteLine($"{attrName}={item.GetValue(u)}"); } Console.WriteLine("********************"); }
版本2:调用方式
foreach (var u in userList) { var uType = u.GetType(); foreach (var item in uType.GetProperties()) { var attrName = item.Name; var ats = from a in item.GetCustomAttributes(typeof(ChineseNameAttribute), true) let a2 = a as ChineseNameAttribute where a2 != null select a2; foreach (var chineseName in ats) //因为这边只有一项 attrName = chineseName.GetChineseName(); Console.WriteLine($"{attrName}={item.GetValue(u)}"); } }
上面就是让我们特性生效的代码。
讲到这里,我们知道了特性大概有啥作用,但是对于特性里面的一些概念和定义还尚未涉及到,所以接下来的时候,讲点特性的相关概念。
(三)特性的概念方面的东西介绍
[AttributeUsage(param)] ,是系统自带的一个特性,用来用来指定当前特性用在哪里,第一次看到这个东西,我们可能不会用,没有关系,我们按照自己的思路来。
下面这个是AttributeUsage特性的源码和中文意思,在VS的F12可以看到各个公开的属性和方法。
通过源码的阅读和Vs里面的一些中文说明,我们可以了解到特性的相关的概念,特性的作用对象是啥,特性是否可以被继承,是否运行多重继承(因为我相信很多同志,估计都记不住这些概念的,但是我们可以按照上面的方法进行查找。
以上就是特性的简单介绍,其实特性的东西远不止这些...接下来会出更多关于这方面的文章,请大家留言讨论。