在C#中反射的实现是使用System.Type类实现的.
System.Type类部分成员
成员 成员类型 描述
Name 属性 返回类型的名字
Namespace 属性 返回包含类型声明的命名空间
Assembly 属性 返回声明类型的程序集。
GetFields 方法 返回类型的字段列表
GetProperties 方法 返回类型的属性列表
GetMethods 方法 返回类型的方法列表
获取Type对象有两种方式
1,Type t = myInstance.GetType();//通过类的实例来获取Type对象
在object类有一个GetType的方法,返回Type对象,因为所有类都是从object继承的,所以我们可以在任何类型上使用GetType()来获取它的Type对象
2,Type t = typeof(ClassName);//直接通过typeof运算符和类名获取Type对象
获取里面的属性
FieldInfo[] fi = t.GetFields();
foreach(FieldInfo f in fi){
Console.WriteLine(f.Name+” “);
}
Assembly类在System.Reflection命名空间中定义,它允许访问给定程序集的元数据,它也包含了可以加载和执行程序集。
如何加载程序集?
1,Assembly assembly1 = Assembly.Load(“SomeAssembly”);根据程序集的名字加载程序集,它会在本地目录和全局程序集缓存目录查找符合名字的程序集。
2,Assembly assembly2 = Assembly.LoadFrom(@”c:\xx\xx\xx\SomeAssembly.dll”)//这里的参数是程序集的完整路径名,它不会在其他位置搜索。
1,获取程序集的全名 string name = assembly1.FullName;
2,遍历程序集中定义的类型 Type[] types = theAssembly.GetTypes();
foreach(Type definedType in types){
//
}
3,遍历程序集中定义的所有特性(稍后介绍)
Attribute[] definedAttributes = Attribute.GetCustomAttributes(someAssembly);
什么是特性?
特性(attribute)是一种允许我们向程序的程序集增加元数据的语言结构。它是用于保存程序结构信息的某种特殊类型的类。
将应用了特性的程序结构叫做目标
设计用来获取和使用元数据的程序(对象浏览器)叫做特性的消费者
.NET预定了很多特性,我们也可以声明自定义特性
应用特性
先看看如何使用特性。特性的目的是告诉编译器把程序结构的某组元数据嵌入程序集。我们可以通过把特性应用到结构来实现。
案例1
[Serializable] //特性
public class MyClass{
// …
}
案例2
[MyAttribute(“Simple class”,”Version 3.57″)] //带有参数的特性
public class MyClass{
//…
}
Obsolete特性->.NET预定义特性
一个程序可能在其生命周期中经历多次发布,而且很可能延续多年。在程序生命周期的后半部分,程序员经常需要编写类似功能的新方法替换老方法。处于多种原因,你可能不再使用哪些调用过时的旧方法的老代码。而只想用新编写的代码调用新方法。旧的方法不能删除,因为有些旧代码也使用的旧方法,那么如何提示程序员使用新代码呢?可以使用Obsolete特性将程序结构标注为过期的,并且在代码编译时,显示有用的警告信息。
class Program{
[Obsolete(“Use method SuperPrintOut”)] //将特性应用到方法
static void PrintOut(string str){
Console.WriteLine(str);
}
[Obsolete(“Use method SuperPrintOut”,true)]//这个特性的第二个参数表示是是否应该标记为错误,而不仅仅是警告。
static void PrintOut(string str){
Console.WriteLine(str);
}
static void Main(string[] args){
PrintOut(“Start of Main”);
}
}
Conditional特性
Conditional特性允许我们包括或取消特定方法的所有调用。为方法声明应用Conditional特性并把编译符作为参数来使用。
定义方法的CIL代码本身总是会包含在程序集中,只是调用代码会被插入或忽略。
#define DoTrace
class Program{
[Conditional(“DoTrace”)]
static void TraceMessage(string str){
Console.WriteLine(str);
}
static void Main(){
TraceMessage(“Start of Main”);
Console.WriteLine(“Doing work in Main.”)
TraceMessage(“End of Main”);
}
}
调用者信息特性
调用者信息特性可以访问文件路径,代码行数,调用成员的名称等源代码信息。
public static void PrintOut(string message,[CallerFilePath] string filename=””,[CallerLineNumber]int lineNumber = 0,[CallerMemberName]string callingMember=””){
Console.WriteLine(“Message:”+message);
Console.WriteLine(“Line :”+lineNumber);
Console.WriteLine(“Called from:”+callingMember);
Console.WriteLine(“Message :”+message);
}
DebuggerStepThrough特性
我们在单步调试代码的时候,常常希望调试器不要进入某些方法。我们只想执行该方法,然后继续调试下一行。DebuggerStepThrough特性告诉调试器在执行目标代码时不要进入该方法调试。有些方法小并且毫无疑问是正确的,在调试时对其反复单步调试只能徒增烦恼。要小心使用该特性,不要排除了可能出现bug的代码。
该特性位于System.Diagnostics命名空间下
该特性可用于类,结构,构造方法,方法或访问器
class Program{
int _x=1;
int X{
get{return _x;};
[DebuggerStepThrough]
set{
_x=_x*2;
_x+=value;
}
}
public int Y{get;set;}
[DebuggerStepThrough]
void IncrementFields(){
X++;
Y++;
}
static void Main(){
Program p = new Program();
p.IncrementFields();
p.X = 5;
Console.WriteLine(“P.X:”+p.X+” p.Y:”+p.Y);
Console.ReadKey();
}
}
其他预定义特性
特性 意义
CLSCompliant 声明可公开的成员应该被编译器检查是否符合CLS。兼容的程序集可以被任何.NET兼容的语言使用
Serializable 声明结构可以被序列化
NonSerialized 声明结构不可以被序列化
DLLImport 声明是非托管代码实现的
WebMethod 声明方法应该被作为XML Web服务的一部分暴露
AttributeUsage 声明特性能应用到什么类型的程序结构。将这个特性应用到特性声明上
多个特性
我们可以为单个结构应用多个特性。有下面两种添加方式
独立的特性片段相互叠在一起
[Serializable]
[MyAttribute(“Simple class”,”Version 3.57″)]
单个特性片段,特性之间使用逗号间隔
[Serializable,MyAttribute(“Simple class”,”Version 3.57″)]
全局特性
我们可以通过使用assembly和module目标名称来使用显式目标说明符把特性设置在程序集或模块级别。
程序集级别的特性必须放置在任何命名空间之外,并且通常放置在AssemblyInfo.cs文件中
AssemblyInfo.cs文件通常包含有关公司,产品以及版权信息的元数据。
[assembly: AssemblyTitle(“ClassLibrary1”)]
[assembly: AssemblyDescription(“”)]
[assembly: AssemblyConfiguration(“”)]
[assembly: AssemblyCompany(“”)]
[assembly: AssemblyProduct(“ClassLibrary1”)]
[assembly: AssemblyCopyright(“Copyright © 2015”)]
[assembly: AssemblyTrademark(“”)]
[assembly: AssemblyCulture(“”)]
自定义特性
应用特性的语法和之前见过的其他语法很不相同。你可能会觉得特性跟结构是完全不同的类型,其实不是,特性只是某个特殊结构的类。所有的特性类都派生自System.Attribute。
声明一个特性类和声明其他类一样。有下面的注意事项
声明一个派生自System.Attribute的类
给它起一个以后缀Attribute结尾的名字
(安全起见,一般我们声明一个sealed的特性类)
特性类声明如下:
public sealed class MyAttributeAttribute : System.Attribute{
…
特性类的公共成员可以是
字段
属性
构造函数
构造函数
特性类的构造函数的声明跟普通类一样,如果不写系统会提供一个默认的,可以进行重载
构造函数的调用
[MyAttribute(“a value”)] 调用特性类的带有一个字符串的构造函数
[MyAttribute(“Version 2.3″,”Mackel”)] //调用特性类带有两个字符串的构造函数
构造函数的实参,必须是在编译期间能确定值的常量表达式
如果调用的是无参的构造函数,那么后面的()可以不写
构造函数中的位置参数和命名参数
public sealed class MyAttributeAttribute:System.Attribute{
public string Description;
public string Ver;
public string Reviewer;
public MyAttributeAttribute(string desc){
Description = desc;
}
}
//位置参数(按照构造函数中参数的位置)
//命名参数,按照属性的名字进行赋值
[MyAttribute(“An excellent class”,Reviewer = “Amy McArthur”,Ver=”3.12.3″)]
限定特性的使用
有一个很重要的预定义特性可以用来应用到自定义特性上,那就是AttributeUsage特性,我们可以使用它来限制特性使用在某个目标类型上。
例如,如果我们希望自定义特性MyAttribute只能应用到方法上,那么可以用如下形式使用AttributeUsate:
[AttributeUsage(AttributeTarget.Method)]
public sealed class MyAttributeAttribute : System.Attribute{
…
AttributeUsage有是三个重要的公共属性如下:
ValidOn 保存特性能应用到的目标类型的列表,构造函数的第一个参数就是AttributeTarget 类型的枚举值
Inherited 一个布尔值,它指示特性是否会被特性类所继承 默认为true
AllowMutiple 是否可以多个特性的实例应用到一个目标上 默认为false