参考源码:https://gitee.com/chenheze90/lc01_-reflect-l/tree/master/
C#反射详解及其性能分析
我们首先盘点一下代码语言的运作过程
- 第一步,用C#写代码
C#是一种高级语言,是人类所用和掌握的开发语言,是为了方便人类开发而生的。但是它并不是直接运行在系统上的。 - 第二步,编译成中间语言
通过VS中的编译器,C#编译成中间语言IL,并打包进.dll文件中。其实dll中除了中间语言IL之外,还包含metadata元数据数据清单,记录了dll中包含了哪些东西,是一个描述。ILSpy是一款正对IL语言的反编译工具,可以将dll中的中间语言反编译成C#; - 第三步,中间语言转化成本地及其语言
这个时候的编译器叫JIT(即时编译器)将IL转化成本地机器语言。
支持软件是第一步到第二部再到第三步。我们本次讲的反射是反过来的,从第二部到第一步。
反射
简单反射获取实例
private static void Reflex1()
{
//第一种方法:直接加载dll
//Assembly assembly = Assembly.LoadFrom("LC01_Reflect.dll");
//第二种方法:提供路径的加载dll
//Assembly assembly = Assembly.LoadFile(@"X://xxx.dll");
//第三种方法:经常用到,被反射的dll存在被替换的情况。本文中的所有情况,都会占用dll资源,导致无法覆盖dll
//byte[] filedata = File.ReadAllBytes("ATL.AutoTestReport.TestItems.dll");
//Assembly assembly = Assembly.Load(filedata);
Assembly assembly = Assembly.Load("LC01_Reflect");//不论是exe还是dll都可以加载
Type type1 = assembly.GetType("LC01_Reflect.Person");
object c = Activator.CreateInstance(type1);
Person s = c as Person;
}
项目内部反射
项目内部反射不再使用Assembly.Load方法,效率提高。
Assembly assembly = Assembly.GetExecutingAssembly();
object obj = assembly.CreateInstance("LC01_Reflect.Person");
//或者
Type type = Type.GetType("LC01_Reflect.Person");
obj = Activator.CreateInstance(type);
Person s = obj as Person;
通过反射调用类内部方法
Assembly assembly = Assembly.Load("LC01_Reflect");
Type type = assembly.GetType("LC01_Reflect.Person");
object oInstance = Activator.CreateInstance(type);
MethodInfo method = type.GetMethod("Say");
// 无参数方法调用
//method.Invoke(oInstance, new object[] { });
//method.Invoke(oInstance, new object[0]);
//method.Invoke(oInstance, null);
// 有参数方法调用
method.Invoke(oInstance, new object[] {"222" });
// 私有方法调用
MethodInfo method2 = type.GetMethod("PSay", BindingFlags.NonPublic | BindingFlags.Instance);
method2.Invoke(oInstance, new object[] { "String" });
// 静态方法
MethodInfo method3 = type.GetMethod("SSay", BindingFlags.Public | BindingFlags.Static);
method3.Invoke(null, new object[] { "333" });
通过反射调用字段及其属性
Assembly assembly = Assembly.Load("LC01_Reflect");
Type type = assembly.GetType("LC01_Reflect.Person");
object oInstance = Activator.CreateInstance(type);
PropertyInfo propertyInfo = type.GetProperty("Name");
propertyInfo.SetValue(oInstance, "sssss");
object resName = propertyInfo.GetValue(oInstance);
FieldInfo filedinfo = type.GetField("age");
filedinfo.SetValue(oInstance, 5);
object resAge = filedinfo.GetValue(oInstance);
控制台代码及其之后结果展示
static void Main(string[] args)
{
//普通反射
Reflex1();
//内部反射
Reflex2();
//通过反射调用方法
Reflex3();
Console.Read();
}
反射的性能
public static void Show()
{
Console.WriteLine("*******************测试性能*******************");
long commonTime = 0;
long reflectionTime = 0;
{
Stopwatch watch = new Stopwatch();
watch.Start();
for (int i = 0; i < 1000_000; i++)
{
Person person = new Person();
//person.Say("Test");
}
watch.Stop();
commonTime = watch.ElapsedMilliseconds;
}
{
Stopwatch watch = new Stopwatch();
watch.Start();
Assembly assembly = Assembly.Load("LC01_Reflect");//1 动态加载
Type type1 = assembly.GetType("LC01_Reflect.Person");//2 获取类型
for (int i = 0; i < 1000_000; i++)
{
object person = Activator.CreateInstance(type1);//3 创建对象
}
watch.Stop();
reflectionTime = watch.ElapsedMilliseconds;
}
Console.WriteLine($"正常new实例化时间:{commonTime}ms, 反射时间:{reflectionTime}ms");
}
测试的结果
实验结果:一百万次的代码执行,实例化是8ms,反射61ms,后者还包含了Load的方法所需的时间。
反射使用的场景和建议
1.反射本身有性能问题,但是影响不大,除非反复使用。所以反射忌讳的是大量的反复使用;
2.反射适合用在架构搭建阶段,它提高了程序的灵活性和扩展性。
3.反射可以用在软件的拓展设计方面给,它降低耦合性,提高自适应能力。
4.可以方便和其他程序的对接,它允许程序创建和控制任何类的对象,无需提前硬编码目标类,不用管理对方的代码而使用对方提供的功能。