C#反射

反射是程序在运行时动态获取信息和调用对象方法的功能。它基于System.Reflection,通过元数据和中间语言实现。反射可用于动态加载DLL、创建对象和访问成员。虽然反射提供灵活性,但其性能较低,适合在需要扩展性和灵活性的场合使用,如IOC容器、MVC框架和ORM。在大量调用时,应考虑缓存以减轻性能影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 反射是什么

1.1 概述

反射是指程序在运行状态中,动态获取程序的信息以及动态调用任意对象的方法的功能。

2. 反射原理

2.1 C#编译过程

C#编译运行过程

DLL/EXE(程序集) : DLL/EXE可以看作是给一堆相关类打一个包,以及一些资源文件的逻辑组合。在 .NET 中,dll与exe文件都是程序集。程序集包含:资源文件,类型元数据,IL代码。

metadate: 元数据数据清单,记录了exe/dll中包含了哪些东西,是一个描述,描述在代码中定义的每一类型和成员,二进制形式。

IL:中间语言,编译器把高级语言编译后得到的C#中最真实的语言状态。

2.2 原理解析

反射技术就是运用System.Reflection,.Net框架提供的帮助类库,可以读取并且使用dll/exe(程序集)的metadate去动态的创建对象,调用成员。

3. 使用反射

3.1 反射用到的命名空间

System.Reflection
//引用type类 --通过这个类可以访问任何给定数据类型的信息
System.Type
//引用 Assembly类 --它可以使用于访问给定程序集的信息,或者把这个程序集加载到程序中。
System.Reflection.Assembly

System.Type类对于反射起着核心的作用。但它是一个抽象的基类,Type有与每种数据类型对应的派生类,我们使用这个派生类的对象的方法、字段、属性来查找有关该类型的所有信息。

3.2 应用示例(简单展示)

  1. 动态读取dll
using System;
using System.Reflection;
public class Example
{
    static void Main()
    {
        //LoadFrom : dll全名称, 需要后缀
        Assembly assembly = Assembly.LoadFrom("Bussiness.DB.SqlServer.dll");
        //loadFile: 全路径, 需要后缀
        Assembly assembly1 = Assembly.LoadFile(@"//dll文件全路径");
        //Load: dll名称不需要后缀
        Assembly assembly2 = Assembly.Load("Bussiness.DB.SqlServer")
    }
}
  1. 反射动态创建对象
//获取某一个具体的类,参数需要是类的全名称
Type type = assmbly.GetType("Business.DB.SqlServer.SqlServerHelper")
//直接传类型创建实例
dynamic dInstance = Activator.CreateInstance(type);
//类型转换
SqlServerHelper helper = dInstance as SqlServerHelper;
//调用方法
helper.Query();
  1. 反射为字段属性赋值取值
Type type = assmbly.GetType("Business.DB.SqlServer.SqlServerHelper")
dynamic dInstance = Activator.CreateInstance(type);
//遍历类的属性 因为是反射所以没办法明指点具体属性
foreach(var prop in type.GetProperties())
{
    if (prop.Name.Equals("Id"))
    {
       //赋值时, 要将实例和value一起传进去
        prop.SetValue(dInstance , 134);
    }
    else if (prop.Name.Equals("Name"))
    {
        prop.SetValue(dInstance , "不要学编程");
    }
    else if (prop.Name.Equals("Age"))
    {
        prop.SetValue(dInstance , 25);
    }             
}

//取值
foreach(var prop in type.GetProperties())
{
     Console.WriteLine($"people.{prop.Name}={prop.GetValue(pObject)}");
}
  1. 使用反射访问特性

C# 使用反射访问特性的操作以及特性详解

3.3 反射的应用场景

  • IOC容器:反射+ 配置文件+ 工厂
  • MVC框架:反射调用类型方法
  • ORM:反射+泛型+Ado.Net
  • AOP:在方法的前面后面添加处理内容,添加通用逻辑

4. 反射的优缺点

4.1 优点:

  • 提高了程序的扩展性和灵活性,反射依赖于配置文件,可以做到不修改代码而改变程序的功能,避免将程序写死到代码里。

  • 降低耦合度,断开了对普通类的依赖,提高自适应能力。

  • 允许动态的创建和使用任何对象,无需提前硬编码目标对象。

4.2 缺点:

  • 性能较低,使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码(直接编码是最快的)。因此反射机制主要应用在对灵活性和扩展性要求很高的系统框架上,普通程序不建议使用。

  • 可读性较低,使用反射会模糊程序内内部逻辑:程序员希望在源代码中看到程序的逻辑,反射等绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂。

  • 内部暴露,破坏封装,由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用--代码有功能上的错误,降低可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。

4.3 反射是否真的会让程序性能降低?

  1. 反射大概比直接调用慢50~100倍,但是需要在执行100万遍的时候才会有所感觉

  2. 判断一个函数的性能,需要把这个函数执行100万遍甚至1000万遍

  3. 如果只是偶尔调用一下反射,请忘记反射带来的性能影响

  4. 如果需要大量调用反射,请考虑反射 + 缓存。

编程思想才是限制程序性能的最主要的因素。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值