C# 特性和索引(C#学习笔记06)
特性
特性(attribute)是用于在运行时传递程序中各种元素(比如类、方法、结构、枚举、组件等)的行为信息的声明性标签。
特性可以当成一个特殊的类看待
列举特性语法:
[attribute(positional_parameters, name_parameter = value, ...)] element
attribute为特性名称,positional_parameters, name_parameter是特性属性,value为name_parameter属性的值
三种预定义特性:
.net framework 提供了三种预定义的特性:
1. attributeusage
该特性描述了用户定义的特性类如何被使用
attributeusage基本结构:
[attributeusage( validon, allowmultiple=allowmultiple, inherited=inherited )]
示例:
[attributeusage(attributetargets.class | attributetargets.constructor | attributetargets.field | attributetargets.method | attributetargets.property, allowmultiple = true)]
validon规定了该特性能够被承载,或者说是能够被那些类型所使用的声明,如示例中指明了该特性只能在class(类),constructor(结构体),field(字段),method(方法),property(属性)
allowmutiple规定是否能被重复使用,如果为true则能被重复使用
inherited规定这个特性是否能被派生类继承,默认false不可继承,true则为可继承
2.conditional
这个预定义特性标记了一个条件方法,其执行依赖于特定的预处理标识符
它会引起方法调用的条件编译,取决于指定的值,比如 debug 或 trace。例如,当调试代码时显示变量的值
conditional的基本结构:
[ conditional( conditionalsymbol )]
使用示例:
#define hong using system; using system.diagnostics; public class myclass { [conditional("hong")] //预定义的conditional特性 public static void message(string msg) { console.writeline(msg); } } class test { static void function1() { myclass.message("in function 1."); function2(); } static void function2() { myclass.message("in function 2."); } public static void main() { myclass.message("in main function."); function1(); } }
该程序预定义了一个宏:hong,conditional特性在函数message中被使用:[conditional("hong")] 如果没有该宏定义则不会执行函数message
1. 若定义了宏则程序如上,运行:(message会被调用)
in main function. in function 1. in function 2. c:\c#\code\conditional\bin\debug\netcoreapp3.0\conditional.exe (进程 18092)已退出,返回代码为: 0。 若要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。 按任意键关闭此窗口...
2. 若将第一行定义宏注释:
//#define hong using system; using system.diagnostics;
运行(此时message函数不会被调用,则没有输出):
c:\c#\code\conditional\bin\debug\netcoreapp3.0\conditional.exe (进程 18264)已退出,返回代码为: 0。 若要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。 按任意键关闭此窗口...
3.obsolete
这个预定义特性标记了不应被使用的程序实体。它可以让您通知编译器丢弃某个特定的目标元素。例如,当一个新方法被用在一个类中,但是您仍然想要保持类中的旧方法,您可以通过显示一个应该使用新方法,而不是旧方法的消息,来把它标记为 obsolete(过时的)。
obsolete特性结构:
[obsolete( message )] [obsolete( message, iserror )]
message:为描述文字,不使用该函数的原因以及替换函数
iserror:为bool值,true则编译器会把引用了该特性的项目当成错误,产生编译器警告
示例:
using system; public class myclass { [obsolete("don't use oldmethod, use newmethod instead", true)] static void oldmethod() { console.writeline("it is the old method"); } static void newmethod() { console.writeline("it is the new method"); } public static void main() { oldmethod(); } }
运行编译器会提示错误:
don't use oldmethod, use newmethod instead
创建自定义特性的步骤:
- 声明自定义特性
- 构建自定义特性
- 在目标程序元素上应用自定义特性
- 通过反射访问特性
详细示例在学习完反射后一同进行
反射
反射(reflection) 对象用于在运行时获取类型信息。该类位于 system.reflection 命名空间中,可访问一个正在运行的程序的元数据。
system.reflection 命名空间包含了允许您获取有关应用程序信息及向应用程序动态添加类型、值和对象的类。
反射(reflection)有下列用途:
- 它允许在运行时查看属性(attribute)信息。
- 它允许审查集合中的各种类型,以及实例化这些类型。
- 它允许延迟绑定的方法和属性(property)。
- 它允许在运行时创建新类型,然后使用这些类型执行一些任务。
查看元数据
using system; [attributeusage(attributetargets.all)] //规定了特性的能承载所有类型 public class helpattribute : system.attribute //自定义特性helpattribute继承自attribute基类 { public readonly string url; public string topic // topic 是一个表示名字的参数 { get { return topic; } set { topic = value; } } public helpattribute(string url) // url 是一个表示位置的参数 { this.url = url; } private string topic; } public class otherattribute : system.attribute { public string topic2 { get { return topic2; } set { topic2 = value; } } } [helpattribute("information on the class myclass")] //特性被应用到myclass一个空类中 [otherattribute()] //第二个特性 class myclass { } namespace attributeappl { class program { static void main(string[] args) { system.reflection.memberinfo info = typeof(myclass); //system.reflection.memberinfo初始化 object[] attributes = info.getcustomattributes(true); //获取目标类(myclass)所承载的特性 for (int i = 0; i < attributes.length; i++) //遍历所有特性 { system.console.writeline(attributes[i]); } } } }
system.reflection.memberinfo info = typeof(myclass);
system.reflection.memberinfo需要初始化,用于与目标类关联(myclass)
结果:(返回了myclass类所承载的两个特性)
helpattribute otherattribute c:\c#\code\feflection\bin\debug\netcoreapp3.0\feflection.exe (进程 6244)已退出,返回代码为: 0。 若要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。 按任意键关闭此窗口...
利用特性设置声明信息并用反射进行访问
using system; using system.reflection; namespace attribute { [attributeusage(attributetargets.class| //声明自定义特性,描述自定义特性debuginfo如何被使用 attributetargets.constructor| attributetargets.field| attributetargets.method| attributetargets.property, allowmultiple =true)] public class debuginfo : attribute //构建自定义特性 { private int bugno; //bug number private string developer; private string lastreview; //last reviewed public string message; public debuginfo(int bn,string d,string lr) //构造函数 { this.bugno = bn; this.developer = d; this.lastreview = lr; } public int bugno { get { return bugno; } } public string developer { get { return developer; } } public string lastreview { get { return lastreview; } } public string message { set { message = value; } get { return message; } } } [debuginfo(45,"asahi","19/5/1",message ="can't return type")] //在目标程序元素上应用自定义特性 [debuginfo(50,"lock","19/9/9",message ="unable variable")] class rectangle //矩形类,承载了两个特性 { protected double length; protected double width; public rectangle(double l,double w) { this.length = l; this.width = w; } [debuginfo(55,"sayo","19/9/15",message ="return false")] public double getarea() { return length * width; } [debuginfo(60,"katsumi","19/10/1",message ="output error")] public void display() { console.writeline("length={0}", length); console.writeline("width={0}", width); console.writeline("area={0}", getarea()); } } class program { static void main(string[] args) { rectangle r = new rectangle(10, 5); r.display(); type type = typeof(rectangle); //获取rectangle的类型 //遍历rectangle特性 console.writeline(" rectangle类型的特性测试:"); foreach(object attributes in type.getcustomattributes(false)) //获取rectangle类型的所有特性,迭代的方式赋予attributes { debuginfo debuginfo = (debuginfo)attributes; //所有特性转换为debuginfo类型 if (debuginfo != null) { console.writeline("debug number:{0}", debuginfo.bugno); console.writeline("developer:{0}", debuginfo.developer); console.writeline("last review:{0}", debuginfo.lastreview); console.writeline("message:{0}\n", debuginfo.message); } } console.writeline(" rectangle类型的所有函数的特性测试:"); //遍历方法属性 foreach(methodinfo m in type.getmethods()) //遍历type类型即rectangle类型所有函数 { foreach(object attributes in m.getcustomattributes(false)) //遍历每个函数的特性 { debuginfo debuginfo = (debuginfo)attributes; if (debuginfo != null) { console.writeline("debug number:{0}", debuginfo.bugno); console.writeline("developer:{0}", debuginfo.developer); console.writeline("last review:{0}", debuginfo.lastreview); console.writeline("message:{0}\n", debuginfo.message); } } } } } }
结果:
length=10 width=5 area=50 rectangle类型的特性测试: debug number:45 developer:asahi last review:19/5/1 message:can't return type debug number:50 developer:lock last review:19/9/9 message:unable variable rectangle类型的所有函数的特性测试: debug number:55 developer:sayo last review:19/9/15 message:return false debug number:60 developer:katsumi last review:19/10/1 message:output error c:\program files\dotnet\dotnet.exe (进程 8772)已退出,返回代码为: 0。 若要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。 按任意键关闭此窗口...
参考链接: