C# 反射

一、概念:

1、反射机制是一种运行时获取类(Type对象)和动态调用对象的成员的机制。
a.可以获取有关已加载的程序集和在其中定义的类型(如类、接口和值类型)的成员信息;
b.可以使用反射在运行时创建指定类的对象,以及调用和访问这些对象的成员。
这种动态获取的信息以及动态调用对象的方法的功能称为反射机制。

 

2、C#编译原理:
(1)C#生成exe文件后,放在bin目录中。bin是binary(二进制)的缩写,但在bin目录中实际放的是IL文件,而不是二进制文件。

 

*添加引用的真实作用:
a.修改了项目的工程文件.csproj(工程文件),在里面添加了被引用的项目所在的位置。
b.那么编译器在生成 宿主程序 (A引用B,则A是宿主) 的时候,会先编译生成 引用的项目,如果引用的项目的程序集生成成功,则把程序集文件 复制到 宿主程序的 bin目录下;
c.在宿主程序中可以直接访问 被引用的项目里的命名空间和类了!(其实是在运行的时候,宿主程序主动将被引用程序加到了宿主程序的应用程序域中了。)


(2)JIT将IL中间语言编译为二进制机械语言,但不是一次性全部编译完,而是用什么就编译什么,未编译前就是一堆字符串。


(3)按F5其实是完成了两个步骤:1.生成exe(F6),2.双击exe时系统用framework运行。
*注册表记录哪种后缀名要用哪种程序打开。程序安装时会在注册表登记这些信息。exe默认用操作系统运行,但是因为操作系统不懂IL,于是交给FW运行。FW运行时会先找Main函数。

*默认配置文件(App.config)不会编译到exe里去,改动后不用重新生成,但要重启程序,因为只在打开时读一次。

(4)exe下载到内存后叫assembly。class对应内存中type的对象。


二、程序集(Assembly) .exe .dll

1.概念
    所有.Net类都是定义在某个Assembly(程序集)中的,.Net基本类是定义在mscorlib.dll中。exe也可以看做是类库,也可以引用。.Net的exe也是Assembly,.Net中的exe和dll的区别就是exe中包含入口函数,其他没有区别,exe也可以当成dll那样引用、也可以反编译。

 

2.方法:
(1)AppDomain.CurrentDomain.GetAssemblies();//获得程序所有的Assembly

 

*应用程序域(AppDomain):一种边界,它由公共语言运行库围绕同一应用程序范围内创建的对象建立(即,从应用程序入口点开始,沿着对象激活的序列的任何位置)。应用程序域有助于将在一个应用程序中创建的对象与在其他应用程序中创建的对象隔离,以使运行时行为可以预知。在一个单独的进程中可以存在多个应用程序域。应用程序域可以理解为一种轻量级进程。起到安全的作用。占用资源小。


(2)Assembly.GetExecutingAssembly();//获取正在执行的 程序集对象

 

(3)Assembly.Load(@"F:\DAL\bin\Debug\DAL");//这个方法通过程序集的长名称(包括程序集名,版本信息,语言文化,公钥标记)来加载程序集的,会加载此程序集引用的其他程序集,一般情况下都应该优先使用这个方法,因为效率最高!

    使用这个方法的时候, CLR会应用一定的策略来查找程序集,实际上CLR按如下的顺序来定位程序集:

a.如果程序集有强名称,在首先在全局程序集缓(GAC)中查找程序集。  (公用的Assembly放到GAC中。)    

b.如果程序集的强名称没有正确指定或GAC中找不到,那么通过配置文件中的<codebase>元素指定的URL来查找

c.如果没有指定强名称或是在GAC中找不到,CLR会探测特定的文件夹:

    假设你的应用程序目录是C:\AppDir,<probing>元素中的privatePath指定了一个路径Path1,你要定位的程序集是AssemblyName.dll则CLR将按照如下顺序定位程序集

          C:\AppDir\AssemblyName.dll

          C:\AppDir\AssemblyName\AssemblyName.dll

          C:\AppDir\Path1\AssemblyName.dll

          C:\AppDir\Path1\AssemblyName\AssemblyName.dll

如果以上方法不能找到程序集,会发生编译错误,如果是动态加载程序集,会在运行时抛出异常!

(4)Assembly.LoadFrom(@"F:\DAL\bin\Debug\DAL.dll");//通过程序集文件路径 会载入dll文件及其引用的其他dll,不能用于加载标识相同但路径不同的程序集。

 

(5)Assembly.LoadFile();//用来加载和检查具有相同标识但位于不同路径中的程序集.但不会加载程序的依赖项。

(6)通过Type.Assembly获取Type所在的程序集对象。
MODEL.Cat cat = new MODEL.Cat();
Type catType = cat.GetType();
Assembly assModel = catType.Assembly;

 

(7)Assembly.GetExecutingAssembly().Location//获得到Exe的全路径 


三、Type类
1.概念:

    Type类可以叫做“类的类”,一个类型对应一个Type类的对象,通过Type对象可以获得类的所有的定义信息,比如类有哪些属性、哪些方法等。Type就是对类的描述。

   (通俗:程序员写的类,在运行时也会变成一个对象,Type的对象。)
   

    JIT生成Type类的对象:只有在程序中第一次遇到某个类的时候,才会去加载这个类,并解析类的内容,然后生成对应的Type对象,并将类解析出来的信息装入Type对象中存入内存,方便以后复用。

   (通俗: MSIL仅仅是一堆字符串,创建Type类对象是为了不用再对MSIL进行解析。)


2.获得Type对象的方法:
(1)通过类获得Type:Type t = typeof(Person)

(2)通过对象获得类的Type:Type t = p.GetType()

(3)调用Assembly的GetTypes()方法可以得到Assembly中定义的所有的类型。

(4)调用Assembly的GetExportedTypes方法可以得到Assembly中定义的所有的public类型。

 

(5)调用Assembly的GetType(name)方法可以得到Assembly中定义的全名为name的类型信息。
Assembly.GetExecutingAssembly().GetType("MyNameSpace.Dog");

* 类放在内存就是type对象:Name:类名,FullName:命名空间.类名
  如果是通过程序集来取,里面的参数必须要是命名空间.类名
  Type t = ass.GetType(“命名空间.类名”);

 

3.Type类的方法:
(1)bool IsAssignableFrom(Type c):判断当前的类型的变量是不是可以接受c类型变量的赋值。(c类型变量是否实现当前类型)
Type typeCat=typeof(Cat);//类
Type typeIBark=typeof(IBark);//接口
Console.WriteLine(typeIBark.IsAssignableFrom(typeCat));//判断类是否实现接口

 

(2)bool IsInstanceOfType(object o):判断对象o是否是当前类的实例(当前类可以是o的类、父类、接口)

(3)bool IsSubclassOf(Type c):判断当前类是否是类c的子类。类的事,没有接口的事。如果当前类是

 

(4)MemSettingsProvider,c是ISettingsProvider,则返回false。

 

 

4.动态调用成员
(1)调用Type的GetProperty方法可以根据属性名获得属性对象PropertyInfo,
主要成员:CanRead、CanWrite、PropertyType
属性类型:SetValue、GetValue:读取值,设置值,第一个参数是实例对象,因为set、get要针对具体实例,最后一个参数null。
type.GetProperty(“Age”).SetValue(p1, 30, null),

(2)调用Type的GetMethod方法可以根据方法名获得方法对象MethodInfo,调用MethodBase 的Invoke方法就可以调用方法,第一个参数是实例对象,第二个参数是参数数组,如果没有参数设置为null。

 

(3)Type类还有很多方法:GetConstructor(获得构造函数)、GetEvent(获得事件)、GetProperties(获得所有属性)等。

 

5.动态创建对象

(1)CreateInstance是创建对应type类型的object对象,要使用要装换成相应的对象

(2)Activator.CreateInstance(Type t)会动态调用类的无参构造函数创建一个对象,返回值就是创建的对象,如果类没有无参构造函数就会报错。(应用:通过配置文件动态创建对象。)

(3)调用有参构造函数,有几个参数就传几个:

Dog d=Activator.CreateInstance(t,"Japan") as Dog;

四、应用:

1、简单反射工厂模式

 public abstract class DALAbsFactory
    {
        /// <summary>
        /// 根据配置文件 创建实体工厂,并以 抽象工厂 返回
        /// </summary>
        /// <returns></returns>
        public static DALAbsFactory GetFactory()
        {
            //1.读取配置文件:<add key="dalFactoryType" value="DalFacotry.DALFactorySql"/>
 string strType = System.Configuration.ConfigurationManager.AppSettings["dalFactoryType"];

            //2.反射创建 实体工厂类对象
            //2.1获取当前运行的程序集,及工厂类型type对象
            Type factoryType = Assembly.GetExecutingAssembly().GetType(strType);
            //2.2判断检测,判断 factoryType  是否为 LogicAbsFactory的子类
            if (typeof(DALAbsFactory).IsAssignableFrom(factoryType))
            {
                //创建类型实例
                return Activator.CreateInstance(factoryType) as DALAbsFactory;
            }
            else
            {
                throw new ArgumentException("您要创建的工厂对象不存在~~~~~~~~!");
            }
        }
    }
*为什么使用配置文件?
因为配置文件可以不编译到程序集中,随时可以进行方便的修改,程序集在运行的时候可以读取配置文件里配置的信息,从而影响程序内部的运行。

 

2、所谓静态,是编译器的玩意,通过反射可以破解!
class Program
    {
        static void Main(string[] args)
        {
            Dog d = new Dog();
            Type p=typeof(Dog);
           MethodInfo s= p.GetMethod("shout");
           s.Invoke(d,null);
           Console.ReadKey();
        }
    }

  class Dog
    {
        public static string voice = "wangwang";

        public static void shout()
        {
            Console.Write("haha");
        }
    }

3、调用private方法,
Person p1 = new Person();
Type type = p1.GetType();
//BindingFlags.Instance表示是实例方法,即不是static方法
//MethodInfo mHaha =  type.GetMethod("Haha",BindingFlags.NonPublic|BindingFlags.Instance);
//mHaha.Invoke(p1, null);

C# 反射是指在运行时动态地获取类的信息,通过反射可以获取类的属性、方法、事件等信息,以及动态创建对象、调用方法、获取值等操作。这使得我们能够在运行时动态地编写代码,可以写出更加灵活和可扩展的程序。 C# 反射的核心是 `System.Reflection` 命名空间,该命名空间包含了许多与反射相关的类和接口,比如 `Type` 类、`MethodInfo` 类、`PropertyInfo` 类等。 下面是一些常用的反射操作: 1. 获取类型信息 可以使用 `typeof` 关键字或者 `Type.GetType()` 方法来获取类型的信息。`typeof` 关键字用于获取编译时已知的类型信息,而 `Type.GetType()` 方法则可以通过类型名称获取运行时的类型信息。 ```csharp // 获取 System.String 类型的信息 Type type1 = typeof(System.String); // 获取指定类型名称的信息 Type type2 = Type.GetType("System.String"); ``` 2. 获取成员信息 可以使用 `Type.GetMembers()` 方法获取类型的所有成员信息,包括属性、方法、字段、事件等。也可以使用 `Type.GetMethod()`、`Type.GetProperty()`、`Type.GetField()`、`Type.GetEvent()` 等方法获取指定成员的信息。 ```csharp Type type = typeof(Person); // 获取类型的所有成员信息 MemberInfo[] members = type.GetMembers(); // 获取指定属性的信息 PropertyInfo property = type.GetProperty("Name"); // 获取指定方法的信息 MethodInfo method = type.GetMethod("SayHello"); // 获取指定字段的信息 FieldInfo field = type.GetField("Age"); // 获取指定事件的信息 EventInfo eventInfo = type.GetEvent("PropertyChanged"); ``` 3. 动态创建对象 可以使用 `Activator.CreateInstance()` 方法动态创建对象,也可以使用 `Type.InvokeMember()` 方法调用构造函数来创建对象。 ```csharp Type type = typeof(Person); // 使用 Activator.CreateInstance() 方法创建对象 Person person1 = (Person)Activator.CreateInstance(type); // 使用 Type.InvokeMember() 方法调用构造函数创建对象 Person person2 = (Person)type.InvokeMember(null, BindingFlags.CreateInstance, null, null, new object[] { "Tom", 18 }); ``` 4. 调用成员 可以使用 `MethodInfo.Invoke()` 方法调用方法,也可以使用 `PropertyInfo.SetValue()` 方法设置属性的值,使用 `FieldInfo.SetValue()` 方法设置字段的值。 ```csharp Type type = typeof(Person); Person person = new Person("Tom", 18); // 调用方法 MethodInfo method = type.GetMethod("SayHello"); method.Invoke(person, null); // 设置属性的值 PropertyInfo property = type.GetProperty("Name"); property.SetValue(person, "Jerry", null); // 设置字段的值 FieldInfo field = type.GetField("Age"); field.SetValue(person, 20); ``` 以上是 C# 反射的一些基本操作,反射的应用非常广泛,可以用来实现插件式开发、ORM 映射等功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值