C#特性以前的时候用过C#中的特性但只是会用什么原理有什么用这些问题不清楚今天就腾出时间学习了一下
C#中的特性使用Attribute描述在使用时就像是java中的批注一样不过C#使用中括号特性用来描述我们的数据编译器能够识别这些特性以附加信息的形式存放在生成的元数据中供clr使用
下边看一个简单的应用view plainprint?
static void Main(string[] args)
{ DisplayRunningMsg()DisplayDebugMsg()Trace(方法执行到结尾了!!)ConsoleRead()
} [DllImport(Userdll)] public static extern int MessageBox(int hParent string msg string Caption int type)[Conditional(DEBUG)] private static void DisplayRunningMsg()
{ ConsoleWriteLine(This is debug)ConsoleWriteLine(开始运行Main子程序当前时间是+DateTimeNow)}
[Conditional(DEBUG)] [Obsolete] private static void DisplayDebugMsg()
{ ConsoleWriteLine(该方法已经废弃啦!!!)}
DllImport特新允许我们引入一个外部的dll下边做一个函数的声明我们就可以调用了
Conditional属性表示在该种条件下就执行下边的代码 所以[Conditional(DEBUG)]此种标识的方法就只有在调试的时候才会在执行 [Obsolete]特性标记该方法已经废弃
运行上述代码输出(在debug模式下)
看的出来程序执行了[Conditional(DEBUG)]标记的方法如果我们debug改为release那么再次执行
程序并没有执行上述方法看的出来由于特性[Conditional(DEBUG)]标记是的在release模式下代码并没有运行其标记的函数那么我们就可以利用这个做一个error trace使其只在debug的模式下输出当前错误信息包括行号方法名位置等这里要用到 stacktrace类
Ok说到这里你应该对特性有了以最最基本的了解
那么究竟什么是特性呢?
其实特性也是一个类比如[Conditional(DEBUG)]就是构造了以Conditional对象(调用构造方法public Conditional(string type) 对DllImport(Userdll)对应的也有一个类DllImport下边我们自定义一个特性你就会明白很多
首先需要定一个一个类 该类需要集成Attribute使其成为一个特性……NET约定特性类都已Attribute结尾然后在该类中定义一下字段和属性完成构造
代码如下view plainprint?
[AttributeUsage(AttributeTargetsAllAllowMultiple = trueInherited = true)] class TrackerAttributeAttribute {
private string opUsernameprivate string opNameprivate DateTime dateTimeprivate string note
public TrackerAttribute(string opUsernamestring opNamestring date)
{ thisopUsername = opUsernamethisopName = opNamethisdateTime = DateTimeParse(date)}
//位置参数通过构造函数传递值public string OpUsername { get { return opUsername } }
public string OpName { get { return opName } }
public DateTime DateTime { get { return dateTime } }
//命名参数提供set public string Note { get { return note } set { note = value } }
public override string ToString()
{ return 操作人 + opUsername + 操作名 + opName + 时间 + dateTime + 备注 + note}
嗯对他和普通的类几乎没什么差别只不过继承于Attribute然后他本身又有一些特性我们做逐一介绍我们在类TrackerAttribute 定义了几个字段完成了构造函数TrackerAttribute(string opUsernamestring opNamestringdate)
那么我么在使用的时候就需要写[Tracker(opusernameopnamenote=这是备注)]嗯是的使用类型(参数值参数值)的方法完成了该对象的构造即调用了该类的构造函数构造函数里与字段对应的参数叫做位置参数因为写的时候必须位置一一与源构造函数相同其他不通过构造函数传入参数传递的叫做命名参数使用字段名=字段值 的形式赋值这样完成函数构造和一些属性的赋值一般情况下我们将位置参数提供get访问而命名参数则提供get和set因为位置参数已经能够同感哦构造函数访问赋值了
这个特性类上边还有几个特性AttributeTargets表示当前特性的作用范围他是一个位标记的枚举比如allfieldmethod标记过后智能在相应的地方做该特性书写比如指定枚举是作用与字段那么如果该特性写在类上边就会报错
如上你的特性类就完成了
这样你就可以在其他方法上做该特性的标记了
我们定义了特性最重要的还是要获得该特性中的值下边是获得的方法view plainprint?
Type type = typeof(Program)object[] objects = typeGetCustomAttributes(false)foreach (var o in objects)
{ TrackerAttribute trackerAttribute = o as TrackerAttributeif (trackerAttribute != null)
ConsoleWriteLine(trackerAttributeToString())else { ConsoleWriteLine(获得对象为空)}
typeGetCustomAttributes(false)该方法将会获得该类上的所有特性标记返回的是一个object的数组你可以遍历然后转换为你的指定特性访问相应字段即可同样你也可以通过typegetMethods()[] 获得一个methodinfo对象然后调用该methodinfo对象的GetCustomAttributes方法即可
介绍了如上的这些我们利用特性实现为枚举增加一个获得其描述的功能
例如定义枚举MyEnummyenum=MyEnumTypeA 调用myenumToDescription 可以得到字符串 类型A我们可以想到定一个描述特性然后在各个枚举元素上做该特性的标记然后提供扩展方法访问该特性取得该特性值
代码如下枚举定义view plainprint?
public enum MyType { [Description(A类型)] TypeA[Description(B类型)] TypeB[Description(C类型)] TypeC }
特性类DescriptionAttribute定义如下view plainprint?
[AttributeUsage(AttributeTargetsFieldAllowMultiple =trueInherited = true)] class DescriptionAttributeAttribute { private string descriptionpublic string Description { get { return description } }
public DescriptionAttribute(String description)
{ thisdescription = description}
指定该特性只用于字段定义DescriptionAttribute(String description)构造函数
Ok现在还缺少枚举的ToDescription方法C#中的枚举是不支持定义方法的
我们可以为其做一个扩展方法扩展方法需要一个静态类参数前要加this 同时也指定了被扩展的对象调用时可使用扩展对像的实例调用也可以使用该静态类来调用详细内容可参考
扩展类如下view plainprint?
public static class Extension {
public static string ToDescription(this MyType myEnum)
{ Type type = typeof (MyType)FieldInfo info= typeGetField(myEnumToString())DescriptionAttribute descriptionAttribute= infoGetCustomAttributes(typeof (DescriptionAttribute) true)[] as DescriptionAttributeif (descriptionAttribute != null)
return descriptionAttributeDescriptionelse return typeToString()}
这样MyType就多了一个ToDescription的方法返回的值就是对应的特性值
在main方法中MyTypemyType = MyTypeTypeBConsoleWriteLine(myTypeToDescription())控制台输出 B类型 达到了我们想要的效果
这个方法还是很有用的
从上边可以看出我们如果要为一些类添加一些附加的信息 这些附加信息在现实意义与该对象并不是具有真正的对象与属性关系 无法在原来的里边添加字段或者加入字段后很难处理这两种情况之一都可以使用特性随后在IL中看一下掉用特性时编译器都做了什么事