阅读目录

前言

我们初学C#的时候看到类上面一对中括号里面有个高亮了的关键字,不知道那是什么有什么用。想问人又不知道它叫什么。纠结的要命。其实,它就是特性。如:

这就是我们今天要分析的主题。

特性是什么?

个人理解,特性就是修饰对象元数据的修饰符。

那么什么是“元数据”?

元数据就是用来描述数据的数据。(挺拗口的)

如:

图中的1.是特性 2.是访问修饰符 3.声明修饰符 4.数据类型 5.变量名 6.变量数据值,其中1、2、3、4、5就是元数据,用来描述数据(6)的数据。

特性到底是什么?

如上面的 Obsolete  ,会不会也是一个如 public  static 这样类似的修饰符呢,我们且看看反编译后的中间语言。

意料之外,我们看到了上面的2、3、4、5,而1(特性)怎么跑到里面去了,且是一种看不懂的东东,反正我们知道了不是类似的修饰符。

然后我们接着在vs里面把光标移到 Obsolete  上按F12,如:

原来只是一个继承了 Attrbute 的一个类(class)。那么上面我们看不懂的部分应该就是这个 ObsoleteAttribute 类的实例化了。

我们来回答上面问题:特性到底是什么?特性只是一个类而已。

我们自定义一个特性玩玩

我们看到上面系统特性 Obsolete 上面还有特性,如:Serializable、AttributeUsage、Camvisible等。像这种特性我们称之为“元数据的元数据”(元元数据)。

1.我们分别来解释性上面的三个特性。

Serializable:表示类型支持序列化。

ComVisible:微软定义“控制程序集中个别托管类型、 成员或所有类型对COM的可访问性”。

AttributeUsage:这个比较重要了,基本上每个特性定义都用到了它。它就是用来表示当前这个特性可用于哪些对象。如:类、方法、属性...等等。(只需要用到这个我们就可以自定义特性了)

2.上面有个问题,不知道大家发现没有。

就是我们特性名明明是 Obsolete  ,为什么我们F12进去后变成了 ObsoleteAttribute 呢?这其实只是一个微软的约定而已,没有为什么。

其实我们可以两种写法: [ObsoleteAttribute("已过时")] 和 [Obsolete("已过时")] 是等效的,只是我们一般都用后面这种。

3.定义的特性必须继承于 Attribute 。

4.属性没有set方法。只能通过构造函数赋值。(这是因为特性语法所致,因为特性的定义只存在单行的中括号中,不能实例化之后在设置属性,所以全部的设置都在后面的小括号里进行的。如果需要有set属性,我们就要用到命名参数,下面会继续讲到)

好了,我们通过这四点完全可以自己定义个特性来玩玩了。我们来定义一个给机器看的注释。我们平时的注释都只是给程序员看的,编译之后就全没了。那我们想在代码运行时,弹出我们的注释怎么办,接下来我们用自定义特性来实现,如:

[AttributeUsage(AttributeTargets.All)]//3.设置可用于哪些对象
public class TMessgAttribute : Attribute//1.定义类TMessg加上后缀TMessgAttribute 2.继承Attribute。
{
public TMessgAttribute() { } /// <param name="createTime">创建时间</param>
/// <param name="createName">创建人</param>
public TMessgAttribute(string createTime, string createName, string mess)
{
this._createName = createName;
this._createTime = createTime;
this._mess = mess;
} private string _createTime;
public string createTime
{
get { return _createTime; }//4.只能有get方法
}
private string _createName;
public string createName
{
get { return _createName; }
}
private string _mess;
public string mess { get { return _mess; } }
}

好了,上面就是我们自定义的特性。那我们怎样使用呢。和系统特性一样。我们先定义一个测试类TClass,然后在类上面定义特性,如:

[TMessg("2015-12-20", "zhaopei", "我只是测试自定义特性,不要报错哦,求求你了。")]
public class TClass
{
//................
}

我们定义了特性,也使用了特性,然我们却不知道怎么看效果。我们想看到效果怎么办。可以使用反射(下篇博问继续分析反射)看看 TClass 类的元数据,如:

static void Main(string[] args)
{
System.Reflection.MemberInfo info = typeof(TClass); //通过反射得到TClass类的信息
TMessgAttribute hobbyAttr = (TMessgAttribute)Attribute.GetCustomAttribute(info, typeof(TMessgAttribute));
Console.WriteLine("类名:{0}", info.Name);
Console.WriteLine("创建时间:{0}", hobbyAttr.createTime);
Console.WriteLine("创建人:{0}", hobbyAttr.createName);
Console.WriteLine("备注消息:{0}", hobbyAttr.mess);
Console.ReadKey();
}

打印效果如:

什么是命名参数?

上面的自定义特性都是通过构造函数设置字段私有字段,然后通过只提供了get的属性来访问。那么可否直接在特性里面定义拥有get和set的属性吗?答案是肯定的。那怎么在使用特性的时候设置这个属性呢?我们接着往下看。

我们接着在自定义特性里面添加一个属性。

/// <summary>
/// 修改时间
/// </summary>
public string modifyTime { get; set; }

使用自定义特性。

[TMessg("2015-12-20", "zhaopei", "我只是测试自定义特性,不要报错哦,求求你了。", modifyTime = "2015-12-21")]
public class TClass
{
//................
}

我们发现,直接在输入了构造函数之后接着设置属性就可以。(这就相当于可选参数了,属性当然可以随便你是否设置了。不过这里需要注意了,前面的参数一定要按照定义的特性构造函数的参数顺序)

这种参数,我们成为命名参数。

我们来继续要看看AttributeUsage(这个描述特性的特性--“元元数据”)

我们F12看看AttributeUsage的定义

看上去,同样也只是普通的特性。实际上也只是个普通的特性。>_<

我们来看看他的这几个属性是干嘛的。从最后一个开始看。

1.AttributeTargets,我们在上面其实就已经看到并也已经使用了。

我们设置的是可用于所有对象。AttributeTargets其实是个枚举,每个值对于一个类型对象。

你可以直接在 AttributeTargets F12进去:

 我们看到了每个值代表可以用于所对于的对象类型。

2.Inherited(是一个布尔值):“如果该属性可由派生类和重写成员继承,则为 true,否则为 false。 默认值为 true”

如下,我们设置 Inherited = false 那么继承TClass的T2Class无法访问到TClass中设置的特性元数据。

namespace net
{
[AttributeUsage(AttributeTargets.All, Inherited = false)]//3.设置可用于哪些对象
public class TMessgAttribute : Attribute//1.定义类TMessg加上后缀TMessgAttribute 2.继承Attribute。
{
public TMessgAttribute() { } /// <param name="createTime">创建时间</param>
/// <param name="createName">创建人</param>
public TMessgAttribute(string createTime, string createName, string mess)
{
this._createName = createName;
this._createTime = createTime;
this._mess = mess;
} private string _createTime;
public string createTime
{
get { return _createTime; }//4.只能有get方法
}
private string _createName;
public string createName
{
get { return _createName; }
}
private string _mess;
public string mess { get { return _mess; } }
/// <summary>
/// 修改时间
/// </summary>
public string modifyTime { get; set; }
} class Program
{
static void Main(string[] args)
{
System.Reflection.MemberInfo info = typeof(T2Class); //通过反射得到TClass类的信息
TMessgAttribute hobbyAttr = (TMessgAttribute)Attribute.GetCustomAttribute(info, typeof(TMessgAttribute));
Console.WriteLine("类名:{0}", info.Name);
if (hobbyAttr != null)
{
Console.WriteLine("创建时间:{0}", hobbyAttr.createTime);
Console.WriteLine("创建人:{0}", hobbyAttr.createName);
Console.WriteLine("备注消息:{0}", hobbyAttr.mess);
Console.WriteLine("修改时间:{0}", hobbyAttr.modifyTime); }
Console.ReadKey();
}
} [TMessg("2015-12-20", "zhaopei", "我只是测试自定义特性,不要报错哦,求求你了。", modifyTime = "2015-12-21")]
public class TClass
{
//................
} public class T2Class : TClass
{
//...........
}
}

反之,我们设置 Inherited = true (或者不设置任何,因为默认就是true)打印如下:

3.AllowMultiple(也是一个布尔值):“如果允许指定多个实例,则为 true;否则为 false。 默认值为 false。”

我们设置两个特性试试,如:

如果我们想要这样设置怎么办。在AttributeUsage中设置 AllowMultiple = true 如:

那么上面报错的地方将会打印:

注意:上面的打印地方的代码需要修改。因为之前是打印一个特性信息,这里是打印一个特性数组集合的信息。

static void Main(string[] args)
{
System.Reflection.MemberInfo info = typeof(T2Class);
TMessgAttribute[] hobbyAttr = (TMessgAttribute[])Attribute.GetCustomAttributes(info, typeof(TMessgAttribute));//修改1.这里需要取特性数据的集合了
Console.WriteLine("类名:{0}", info.Name);
for (int i = 0; i < hobbyAttr.Count(); i++)//修改2.这里需要循环打印了
{
Console.WriteLine("================================================");
Console.WriteLine("创建人:{0}", hobbyAttr[i].createName);
Console.WriteLine("创建时间:{0}", hobbyAttr[i].createTime);
Console.WriteLine("备注消息:{0}", hobbyAttr[i].mess);
Console.WriteLine("修改时间:{0}", hobbyAttr[i].modifyTime);
} Console.ReadKey();

全部代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks; namespace net
{
[AttributeUsage(AttributeTargets.All, Inherited = true, AllowMultiple = true)]//3.设置可用于哪些对象
public class TMessgAttribute : Attribute//1.定义类TMessg加上后缀TMessgAttribute 2.继承Attribute。
{
public TMessgAttribute() { } /// <param name="createTime">创建时间</param>
/// <param name="createName">创建人</param>
public TMessgAttribute(string createTime, string createName, string mess)
{
this._createName = createName;
this._createTime = createTime;
this._mess = mess;
} private string _createTime;
public string createTime
{
get { return _createTime; }//4.只能有get方法
}
private string _createName;
public string createName
{
get { return _createName; }
}
private string _mess;
public string mess { get { return _mess; } }
/// <summary>
/// 修改时间
/// </summary>
public string modifyTime { get; set; }
} class Program
{
static void Main(string[] args)
{
System.Reflection.MemberInfo info = typeof(T2Class);
TMessgAttribute[] hobbyAttr = (TMessgAttribute[])Attribute.GetCustomAttributes(info, typeof(TMessgAttribute));//修改1.这里需要取特性数据的集合了
Console.WriteLine("类名:{0}", info.Name);
for (int i = 0; i < hobbyAttr.Count(); i++)//修改2.这里需要循环打印了
{
Console.WriteLine("================================================");
Console.WriteLine("创建人:{0}", hobbyAttr[i].createName);
Console.WriteLine("创建时间:{0}", hobbyAttr[i].createTime);
Console.WriteLine("备注消息:{0}", hobbyAttr[i].mess);
Console.WriteLine("修改时间:{0}", hobbyAttr[i].modifyTime);
} Console.ReadKey();
}
} [TMessg("2015-12-20", "zhaopei", "我只是测试自定义特性,不要报错哦,求求你了。", modifyTime = "2015-12-21")]
[TMessg("2015-12-21", "zhaopei", "我再次测试,还能给我面子显示出来吗?", modifyTime = "2015-12-22")]
public class TClass
{
//................
} public class T2Class : TClass
{
//...........
}
}

自定义特性可以干什么?

上面我们通过反编译,发现自定义特性实际上就是一个对象调用的最前面加了一段实例化的代码。

那么我们可以做的事可多了,除了像上面一样为对象设置“注释”,我们还可以自定义个特性,给某些方法或是某些类做“操作日志记录”,或者给需要在执行某些方法的时候需要权限,我们可以做个权限认证的特性等等。

这里就需要大家自己去扩展了。

[巩固C#] 一、特性是什么东东的更多相关文章

  1. Lambda 表达式 是 个 好东东

    Lambda 表达式 是 个 好东东 首先,通过 Lambda 表达式 + 动态语言特性 dynamic , C# 已经 可以 实现 函数式 编程 了 其次, 利用 Lambda, 可以 实现 AOP ...

  2. 【转】DSP是什么--DSP是神马东东??

    原文:http://www.eepw.com.cn/article/272908.htm 导读:本文主要介绍的是DSP是什么,不懂得童鞋们快随小编一起学习一下DSP到底是个神马东东吧! 本文引用地址: ...

  3. DNS是个什么东东

    工作中经常用到或者听到DNS,什么内网服务器,DNS服务器,啥也别想,你问下自己,你知道什么是DNS吗? 如果你非常清楚,OK,那你肯定是鼠标点错了,赶紧关了这个页面吧,如果你不是很清楚,知道一点,那 ...

  4. json是个啥东东

    xml 不用说 只要是搞web开发的 没听说谁不知道的 一种类似数据传输格式定义的语言 但是他却不是一个真正的轻量级的东西 其他的不说 只要传输一点很少的数据 经过他那左括号右括号 还有什么属性 一包 ...

  5. 长见识了,知道了collected和Graphite 这两个东东

    今天下午的讨论会议中,听到了两个名词collected和Graphite这是神马东东,以前在bingo的时候也没听说过,开完会下去查了下.原来他两是监控系统的啊.以前也从来没做过系统监控方面的项目,这 ...

  6. BPEL是个什么东东

    研究团队有个做智能服务组合的,其中用到叫BPEL的东西,因为全称是Business Process Execution Language,译成中文就是商业执行过程语言,这个东东的是整合SOA的一个执行 ...

  7. SQLSERVER 里经常看到的CACHE STORES是神马东东?

    SQLSERVER 里经常看到的CACHE STORES是神马东东? 当我们在SSMS里执行下面的SQL语句清空SQLSERVER的缓存的时候,我们会在SQL ERRORLOG里看到一些信息 DBCC ...

  8. 【UVA 1151】 Buy or Build (有某些特别的东东的最小生成树)

    [题意] 平面上有n个点(1<=N<=1000),你的任务是让所有n个点连通,为此,你可以新建一些边,费用等于两个端点的欧几里得距离的平方. 另外还有q(0<=q<=8)个套餐 ...

  9. JSNI GWT中的东东

    二.JavaScript Native InterfaceJavaScript本地接口JSNI.1)声明一个本地方法在JSNI中声明一个本地方法时,使用Java的标准native关键字,就像在JNI( ...

随机推荐

  1. 几款Web服务器性能压力测试工具

    一.http_load 程序非常小,解压后也不到100K http_load以并行复用的方式运行,用以测试web服务器的吞吐量与负载. 但是它不同于大多数压力测试工具,它可以以一个单一的进程运行,一般 ...

  2. CentOS6.5上Zabbix3.0的RPM安装【二】-汉化

    六.汉化 zabbix实际是有中文语言的,我们可以通过修改web端源文件来开启中文语言.首先点击zabbix监控页面右上角管理员头像进入“用户基本资料设置页面“. 选择中文语言. 点击“Update” ...

  3. bzoj 1013: [JSOI2008]球形空间产生器sphere【高斯消元】

    n+1个坐标可以列出n个方程,以二维为例,设圆心为(x,y),给出三个点分别是(a1,b1),(a2,b2),(a3,b3) 因为圆上各点到圆心的距离相同,于是可以列出距离方程 \[ (a1-x)^2 ...

  4. loj #2008. 「SCOI2015」小凸想跑步

    #2008. 「SCOI2015」小凸想跑步   题目描述 小凸晚上喜欢到操场跑步,今天他跑完两圈之后,他玩起了这样一个游戏. 操场是个凸 n nn 边形,N NN 个顶点按照逆时针从 0∼n−1 0 ...

  5. ReentrantReadWriteLock原理

    原文链接:https://www.jianshu.com/p/9f98299a17a5 前言 本篇适用于了解ReentrantLock或ReentrantReadWriteLock的使用,但想要进一步 ...

  6. jpa batch批量操作save和persist比较

    1.网上最常见的JPA----entityManager批量操作方法 private EntityManager em; @PersistenceContext(name = "Entity ...

  7. 微信小程序强制横屏办法

    最近想学习学习微信小程序开发,本着先设计,再查找具体实现的方法的想法,在进行数据统计时,想着竖屏展示数据会造成重叠,或者数据显示不全而用省略号代替的问题,所以计划采用横屏的方式显示数据表格. 搜索到两 ...

  8. 公有云Docker镜像P2P加速之路:安全篇

    一.问题 在使用Docker运行容器化应用时,宿主机通常先要从Registry服务(如Docker Hub)下载相应的镜像(image).这种镜像机制在开发环境中使用还是很有效的,团队成员之间可以很方 ...

  9. 关于STM32F407启动后的系统时钟频率问题

    玩STM32的时间也比较久了,最早的一直玩的是STD标准库的103系列,但是ST公司也是“与时俱进”,舍弃了当年的标准库,转而推广HAL库,反正无论怎么样把,对于STM32的使用也仅仅停留在使用阶段, ...

  10. C# 实现retry

    C# 有try-catch ,但是没有retry 功能,通过用有限次循环的办法来模拟Retry,当然中间需要加一个等待的过程. 我们可以利用C#的匿名方法(anonymous methods)和匿名委 ...