一.什么是反射?
程序在运行时,可以查看其它程序集或其本身的元数据一个运行的程序查看本身的元数据或者其他程序集的元数据叫做反射。
注:**元数据:**C# 编写的程序编译成一个程序集,程序集会包含元数据、编译代码和资源。
元数据包含内容:
- 清单信息,包括与程序本身有关的数据,以及它依赖的库;
- 程序或类库中每一个类型的描述;
- 在代码中嵌入的自定义特性,提供与特性所修饰的构造有关的额外信息。
二.Type类
预定义类型(int long和string等),BCL中的类型(Console,IEnumerable)和程序员和程序员自定义类型(MyClass,MDel等)每种类型都有自己的成员和特性。
BCL声明了一个叫做Type的抽象类,它被设计用来包含类型的特性。使用这个类的对象能让我们获取程序使用的类型的信息。
由于Type是抽象类,因此不能利用它实例化对象。
代码实现:
首先新建一个MyClass类:
class MyClass
{
private int id;
private int age;
public int number;
private string Name { get; set; }
public void Test1()
{
}
public void Test2()
{
}
获取MyClass类的Type对象:
class Program
{
static void Main(string[] args)
{
//每一个类对应一个Type对象,这个Type对象存储了这个类的方法、数据成员
MyClass myClass=new MyClass();
Type type = myClass.GetType();//获取这个类的Type对象
Console.WriteLine("名字:"+type.Name);//获取类的名字
Console.WriteLine("命名空间:"+type.Namespace);//获取类所在的命名空间
Console.WriteLine("程序集:"+type.Assembly);//程序集
FieldInfo[] array= type.GetFields();//只能获取public字段
foreach (FieldInfo info in array) //遍历所有的字段
{
Console.WriteLine("字段:"+info.Name+" ");
}
PropertyInfo[] array2 = type.GetProperties();
foreach (PropertyInfo info in array2)
{
Console.WriteLine("属性:"+info.Name+" ");
}
MethodInfo[] array3 = type.GetMethods();
foreach (MethodInfo info in array3)
{
Console.WriteLine("方法:"+info.Name+" ");
}
Console.ReadKey();
}
运行结果:
三、程序集-Assembly类
一般情况下一个项目有一个程序集,这个程序集是按照项目名命名的。
程序集查看方法:
右键项目–>属性–>生成
按照路径找到程序集:
代码实现:
class Program
{
static void Main(string[] args)
{
MyClass my=new MyClass();
Assembly assem = my.GetType().Assembly;//通过类的Type对象获取他所在的程序集
Console.WriteLine(assem.FullName);
Type[] types = assem.GetTypes();
foreach (Type type in types)
{
Console.WriteLine(type);
}
Console.ReadKey();
}
运行结果:
四.特性
1.Obsolete特性
一个程序可能在其生命周期中经历多次发布,而且很可能延续多年。在程序生命周期的后半部分,程序员经常需要编写类似功能的新方法替换老方法。处于多种原因,你可能不再使用哪些调用过时的旧方法的老代码。
可以使用Obsolete特性将程序结构标注为过期的,并且在代码编译时,显示有用的警告信息。
示例:
class Program
{
[Obsolete("此方法已经不用了",true)] //true代表已经把老方法标记为错误,不可以调用。如果第二个参数为false就可以继续调用此方法
static void oldMethod()
{
Console.WriteLine("oldMethod");
}
static void newMethod()
{
}
static void Main(string[] args)
{
oldMethod();//obsolete第二个参数为true时,就不可以调用
}
调用老的方法就会提示“此方法已经不可以用了”
2.Conditional特性:
Conditional特性允许我们包括或取消特定方法的所有调用。为方法声明应用Conditional特性并把编译符作为参数来使用。
示例:
class Program
{
static void Test1()
{
Console.WriteLine("Test1");
}
static void Test2()
{
Console.WriteLine("Test2");
}
static void Main(string[] args)
{
Test1();
Test2();
Test1();
}
}
此时可以成功调用,Test1和Test2方法
如果我在Test1方法上面添加Conditional属性:
class Program
{
[Conditional("IsTest")]
static void Test1()
{
Console.WriteLine("Test1");
}
static void Test2()
{
Console.WriteLine("Test2");
}
static void Main(string[] args)
{
Test1();
Test2();
Test1();
}
}
此时,Test1方法就不可以被调用
如果我们定义一个宏,那么Test1方法会重新被调用:
#define IsTest //定义的一个宏
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _403_特性
{
class Program
{
[Conditional("IsTest")]
static void Test1()
{
Console.WriteLine("Test1");
}
static void Test2()
{
Console.WriteLine("Test2");
}
static void Main(string[] args)
{
Test1();
Test2();
Test1();
Console.ReadKey();
}
}
}
3.调用者信息特性
调用者信息特性可以访问文件路径,代码行数,调用成员的名称等源代码信息。这三个特性名称为CallerFilePath,CallerLineNumber,CallerMemberName,这些方法特性只能用于方法中的可选参数。
应用示例:
class Program
{
static void Test3(string str,[CallerFilePath] string fileName=" ",
[CallerLineNumber] int lineNumber=0,[CallerMemberName] string methodName=" ")
{
Console.WriteLine(str);
Console.WriteLine(fileName);
Console.WriteLine(lineNumber);
Console.WriteLine(methodName);
}
static void Main(string[] args)
{
Test3("haha");
Console.ReadKey();
}
}
结果输出分别为:
①调用完整的路径名
②调用行数
③调用方法名
4.DebuggerStepThrough特性
我们在单步调试代码的时候,常常希望调试器不要进入某些方法(例如在调试时,确定没有错误的代码)。这时我们可以使用DebuggerStepThrough特性。