C#基础--反射

反射

一、为什么学习反射

因为反射真的是无处不在,ORM、MVC、IOC、AOP、Attribute等等都会使用到反射。反射是程序员的快乐

二、什么是反射

image-20220302185357180

Ilspy:逆向工程,可以吧DLL/Exe文件反编译回来

image-20220302190153414

DLL/EXE 文件下包含Metadata和IL,IL是对标于C#代码的代码,属于中间语言,是标准的面向对象语言

image-20220302190125450

而Metadata(元数据)是一个清单数据,只是描述了类中有什么,而不是展示所有的实现。一般我们用F12查看元数据,可以发现只有方法体没有声明:

image-20220302190537206

反射是一种工具,命名空间为System.Reflection,可以读取metadata,并使用metadata。(是微软提供的一个帮助类库)

三、Type类

3.1 简介

​ BCL声明了一个叫做Type的抽象类,它被设计用来包含类型的特性,使用这个类的对象能让我们获取程序使用的类型的信息。

​ 由于Type是抽象类,因此它不能有实例,而是在运行时CLR创建从Type(RuntimeType)派生的类的实例。Type包含了类型信息,当我们要访问这些实例时,CLR不会返回派生类的引用而是返回Type基类的引用。为了简单起见,下面篇幅中我会把引用所指向的对象称为Type类型的对象。

Type类的重要事项:

​ ● 对于程序中用到的每一个类型,CLR都会创建一个包含这个类型信息的Type类型的对象

​ ● 程序中用到的每一个类型都会关联到独立的Type类型的对象

​ ● 不管创建的类型有多少个实例,只有一个Type对象会关联到所有这些实例

​ 如果你对C#中有哪些类型不是很了解,那么知识扩展: C#中的类型有:预定义的类型(int,long和string等),BCL中的类型(Console,IEnumerable等)以及用户自定义的类型(MyClass,MyDel等)

3.2 Type类的部分常见成员

成员成员类型描述
Name属性返回类型的名字
FullName属性返回数据类型的完全限定名(包括命名空间名)
NameSpace属性返回包含数据类型声明的命名空间
Assembly属性返回声明类型的程序集。如果类型是泛型的,返回定义这个类型的程序集
GetConstructor(), GetConstructors()方法返回ConstructorInfo类型,用于取得该类的构造函数的信息
GetEvent(), GetEvents()方法返回EventInfo类型,用于取得该类的事件的信息
GetField(), GetFields()方法返回FieldInfo类型,用于取得该类的字段(成员变量)的信息
GetInterface(), GetInterfaces()方法返回InterfaceInfo类型,用于取得该类实现的接口的信息
GetMember(), GetMembers()方法返回MemberInfo类型,用于取得该类的所有成员的信息
GetMethod(), GetMethods()方法返回MethodInfo类型,用于取得该类的方法的信息
GetProperty(), GetProperties()方法返回PropertyInfo类型,用于取得该类的属性的信息

3.3 获取Type对象

都是获取类的引用的数据类型 System.Type

  • GetType()方法继承自Object,所以C#中任何对象都具有GetType()方法,x.GetType(),其中x为变量名

  • typeof(x)中的x,必须是具体的类名、类型名称等,不可以是变量名称

  • System.Type.GetType(),有两个重载方法:

    • 比如有这样一个变量i,Int16 i = new Int16();使用GetType(),i.GetType()返回值是Int16的类型;但是无法使用typeof(i),因为i是一个变量
    • 使用typeof(),则只能:typeof(Int32),返回的同样是Int16的类型
    • 枚举类的转换,String——枚举类,以串口中Parity为例。objParity= (Parity)Enum.Parse(typeof(Parity), “9600”);

获取类的Type属性,除了上面的使用实例获取,或者typeof,还有一种提供类的完整信息字符串,根据类名来获取Type:

//取得当前方法命名空间
str += "命名空间名:" + System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Namespace + "\n";
//取得当前方法类全名 包括命名空间
str += "类名:" + System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName + "\n";
//取得当前方法名
str += "方法名:" + System.Reflection.MethodBase.GetCurrentMethod().Name + "\n"; str += "\n";
//取得当前方法类全名 包括命名空间
string classname = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName; //得到的是 命名空间.类
Type type = Type.GetType(classname); //通过类名获取同名类

object obj = System.Activator.CreateInstance(type); //创建实例

四、反射基本操作

4.1 反射–动态加载dll

  1. 通过dll文件名称进行加载

    Assembly assembly = Assembly.Load("DB.SqlServer");// dll名称  默认到当前目录下查找
    
  2. 全名称= 全路径+dll名称 + 后缀

    Assembly assembly = Assembly.LoadFile(@"D:\软谋教育\Ruanmou\Advanced13Encrypt\20191010Advanced13Course3Reflection\20191010Advanced13Course3Reflection\MyReflection\MyReflection\bin\Debug\DB.SqlServer.dll");
    
  3. 全名称

    Assembly assembly = Assembly.LoadFrom("DB.SqlServer.dll");
    
  4. 全名称

    Assembly assembly = Assembly.LoadFrom(@"D:\软谋教育\Ruanmou\Advanced13Encrypt\20191010Advanced13Course3Reflection\20191010Advanced13Course3Reflection\MyReflection\MyReflection\bin\Debug\DB.SqlServer.dll");
    

4.2 常规玩法

//1.动态加载dll
Assembly assembly = Assembly.Load("DB.SqlServer");// dll名称  默认到当前目录下查找

//获取所有类型
//assembly.GetTypes() 
//2.通过类型名称获取指定类型
Type type = assembly.GetType("DB.SqlServer.SqlServerHelper");

//3.创建对象
object oDbHelper = Activator.CreateInstance(type);
//dynamic dDbHelper = Activator.CreateInstance(type);
//dDbHelper.Query();

//4.类型转换
IDBHelper iDBHelper = oDbHelper as IDBHelper;

//5.调用方法
iDBHelper.Query();

第二步获取类型时,形式 = 命名空间名称 + 类名

第三步创建对象,其实和IDBHelper dBHelper = new SqlServerHelper(); 实例化一个对象等价,会执行到SqlServerHelper的构造函数里

经过第三步创建对象之后并不能直接调用Query方法,因为编译器就不认可(c# 是强类型语言);dynamic 是一个动态类型,可以避开编译器的检查,运行时检查,但存在安全问题 。

4.3 进阶玩法

封装 Factory + config 代码可以不用编译发布

简单工厂:

public class SimlpFactory
{ 
    private static string IDBHelperConfig = ConfigurationManager.AppSettings["IDBHelperConfig"]; 
    private static string DllName = IDBHelperConfig.Split(',')[1];
    private static string TypeName = IDBHelperConfig.Split(',')[0]; 
    public static IDBHelper CreateInstentce()
    {
        Assembly assembly = Assembly.Load(DllName);
        Type type = assembly.GetType(TypeName);
        object oDbHelper = Activator.CreateInstance(type);
        return oDbHelper as IDBHelper;
    }
}

config文件:

<appSettings>
    <add key="IDBHelperConfig" value="DB.Orcale.OrcaleHelper,DB.Orcale"/>
</appSettings>

调用:

IDBHelper iDBHelper = SimlpFactory.CreateInstentce();
iDBHelper.Query(); 

4.4 反射–选择不同的构造函数创建对象

//1.动态加载
Assembly assembly = Assembly.Load("DB.SqlServer");
//2.获取类型
Type type = assembly.GetType("DB.SqlServer.ReflectionTest");
//3.创建对象
object oDbHelper = Activator.CreateInstance(type);	//public ReflectionTest()
object oDbHelper1 = Activator.CreateInstance(type, new object[] { "杰克" });	// public ReflectionTest(string name)
object oDbHelper3 = Activator.CreateInstance(type, new object[] { 123 });	//public ReflectionTest(int id)

五、反射调用方法

5.1 反射–非类型转换调用方法

Assembly assembly = Assembly.Load("DB.SqlServer");
Type type = assembly.GetType("DB.SqlServer.ReflectionTest");

object oTest = Activator.CreateInstance(type);

无参数方法

MethodInfo show1 = type.GetMethod("show1");
show1.Invoke(oTest, null); //反射调用方法

有参数方法

MethodInfo show2 = type.GetMethod("show2");
show2.Invoke(oTest, new object[] { 123 }); //反射调用方法

重载方法,方法名称一样,参数不同

MethodInfo show1 = type.GetMethod("show1", new Type[] { typeof(string), typeof(int) });
show1.Invoke(oTest, new object[] { 123, "陈贺章" });

私有方法:

MethodInfo show4 = type.GetMethod("show4", BindingFlags.NonPublic | BindingFlags.Instance);
show4.Invoke(oTest, new object[] { "私有方法" });

静态方法: 对象的实例可以传入,也可以不传入。如果像常规使用是不需要实例化对象就可以直接调用方法(反射传null),但是反射却可以传入对象的实例

MethodInfo show5 = type.GetMethod("show5");
show5.Invoke(oTest, new object[] { "我是静态方法" });
show5.Invoke(null, new object[] { "我是静态方法" });

5.2 反射–调用泛型方法

六、反射黑科技

假设我们存在一个单例模式,代码如下:

/// <summary>
/// 单例模式:类,能保证在整个进程中只有一个实例
/// </summary>
public sealed class Singleton
{
    private static Singleton _Singleton = null;
    private Singleton()
    {
        Console.WriteLine("Singleton被构造");
    }

    static Singleton()
    {
        _Singleton = new Singleton();
    }

    public static Singleton GetInstance()
    {
        return _Singleton;
    }
}

常规用法:

Singleton singleton1 = Singleton.GetInstance();
Singleton singleton2 = Singleton.GetInstance();
Console.WriteLine(singleton1.Equals(singleton2)); //结果为:true

反射应用: 两次对象不一致,Activator.CreateInstance完全等价于new Singleton(),每次都调用了私有构造函数。反射破坏了单例,其实就是反射调用了私有构造函数

Assembly assembly = Assembly.Load("DB.SqlServer");
Type type = assembly.GetType("DB.SqlServer.Singleton");
object oSingleton1 = Activator.CreateInstance(type, true);   //第二个参数为 NoPublic--是否为公有的,完全等价于new Singleton();
object oSingleton2 = Activator.CreateInstance(type, true);
Console.WriteLine(oSingleton1.Equals(oSingleton2)); //结果为:false
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

武功山上捡瓶子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值