C# Reflection笔记

反射

1. 程序集里的元数据

C:\Program Files (x86)\Microsoft SDKs\Windows\v8.0A\bin\NETFX 4.0 Tools\x64\ildasm.exe
用这个工具打开一个assembly。
file -> dump -> dump metainfo, 保存到dump.txt中, 看这个文件。
编译生成il代码,和类的metainfo。

AppDomain

一个程序运行起来以后,有一个AppDomain,在这个AppDomain中放了我们用到的所有assembly。
C# Reflection笔记_第1张图片

需要using System.Reflection;

Assembly的组成

C# Reflection笔记_第2张图片

2. 反射

反射概念:

在程序运行时,动态获取 程序集, 类型(class,interface)和类型的成员信息(方法,字段,属性等)。
在程序运行时,动态创建 类型实例, 以及调用和方法 动态创建出来的 类型实例的成员。
反射的应用:框架(Spring .net/ .Net MVC等)
在程序运行时,动态获取 程序集:

    class Program
    {
        static void Main(string[] args)
        {
            Assembly[] ass = AppDomain.CurrentDomain.GetAssemblies();
            Person p = new Person();
            p.TestAssembly();
        }
    }


    class Person {
        public String Name { get; set; }
        public int Age { get; set; }
        private int id;

        public void TestAssembly() {
            Assembly ass = this.GetType().Assembly;
            Type[] types = ass.GetTypes();
            Type currentType = ass.GetType();
            //根据类的full name,获取在内存中的类型对象
            Type typeByFullName = ass.GetType("ConsoleApplication6.Person");


            Type type = this.GetType();
            MethodInfo[] methods = this.GetType().GetMethods();
            this.GetType().GetMembers();
            this.GetType().GetMember("Name");
            FieldInfo field =  type.GetField("id");
            PropertyInfo prop = type.GetProperty("Name");


        }
    }
在程序运行时,动态创建 类型实例:在Person类中加下面4个方法,在Main中测试
        public void CreatePersonObject() {
            Type type = this.GetType();
            //使用Person类的Type对象,创建一个Person对象
            Person p = Activator.CreateInstance(type) as Person;
            Person p1 = Activator.CreateInstance();

            //使用Person类的Name属性对象,为p的Name属性赋值
            PropertyInfo prop = type.GetProperty("Name");
            prop.SetValue(p1, "toto");
            Console.WriteLine(p1.Name);

            //使用Person类的Sayhi方法对象,调用p1的Sayhi方法
            MethodInfo method = type.GetMethod("Sayhi");
            method.Invoke(p1, null);
            MethodInfo method1 = type.GetMethod("ShowNumber");
            object[] arrParams = {2};
            method1.Invoke(p1, arrParams);
            MethodInfo method2 = type.GetMethod("GetString");
            String retStr = method2.Invoke(p1, null).ToString();
            Console.WriteLine(retStr);
        }

        public void Sayhi() {
            Console.WriteLine("Hiiiiii");
        }

        public void ShowNumber(int no) {
            Console.WriteLine(no);
        }

        public String GetString() {
            return "Hello";
        }

在Main中
            Person p = new Person();
            p.CreatePersonObject();

3. Assembly 程序集对象

Assembly 是一个抽象类,我们用的都是RuntimeAssembly的对象。

获得程序集的方式:

  • 获得当前程序域中的所有程序集
    • Assembly[] ass = AppDomain.CurrentDomain.GetAssemblies();
    • 所有用到过得aessembly。如果只是add ref了,没有在程序中用到,AppDomain.CurrentDomain.GetAssemblies()中没有。用到时才被JIT加载到内存。
    • 每个app都有一个AppDomain,OS不允许其他app访问这个程序的AppDomain
  • 获得当前对象所属的类所在的程序集
    • this.GetType().Assembly;
    • Type对象肯定在一个assembly对象中
    • 可以通过Type对象得到程序集。
  • 根据路径加载程序集
    • Assembly.LoadFrom(assPath);
    •         static void LoadAssemblyTest() {
                  Assembly ass1 = Assembly.LoadFrom(@"c:\users\xcao\documents\visual studio 2012\Projects\ConsoleApplication6\ConsoleApplication6\libs\Ecole.dll");
                  Type[] allTypes = ass1.GetTypes();
                  //GetType的参数一定要是full name的string
                  Type tStu = ass1.GetType("Ecole.Student");
                  object stu1 = Activator.CreateInstance(tStu);
              }


4. Type 类型对象

Type 是一个抽象类,我们用的都是TypeInfo类的对象。

程序运行时,一个class对应一个Type类的对象。通过Type对象可以获得类的所有信息。

获得Type对象的方式:

  • 通过类获得对应的Type
    • Type t1 = typeof(Person);
  • 通过对象获得Type
    • Type t2 = person.GetType();  this.GetType();
  • 用assembly对象,通过类的full name类获得type对象
    •             Assembly ass1 = Assembly.LoadFrom(@"c:\users\xcao\documents\visual studio 2012\Projects\ConsoleApplication6\ConsoleApplication6\libs\Ecole.dll");
                  
                  //GetType的参数一定要是full name的string
                  Type tStu = ass1.GetType("Ecole.Student");
                  object stu1 = Activator.CreateInstance(tStu);

  • 获得程序集中定义的所有的public类
    • Type[] allPublicTypes = ass1.GetExportedTypes();
  • 获得程序集中定义的所有的类
    • Type[] allTypes = ass1.GetTypes();

Type类的属性:

    • t.Assembly; 获取t所在的程序集
    • t.FullName; 获取t所对应的类的full name
    • t.Name; 获取t所对应的类的 name
    • t.IsArray; 判断t是否是一个数组类
    • t.IsEnum; 判断t是否是一个枚举类
    • t.IsAbstract; 判断t是否是一个抽象类
    • t.IsInterface; 判断t是否是一个interface

Type类的方法:

    • notebookInterfaceType.IsAssignableFrom(Type t);判断t是否实现了 notebookInterfaceType 接口
    • t.IsSubclassOf(Type parent); t是否是parent的子类
    • t.IsInstanceOfType(object o); o是否是t类的对象
    • t.GetFields();  //method, property 得到所有的public的fields,methods,properties
    •  t.GetField("gender"); 根据名字得到某个field
Type类的示例1:
        static void TypeTest1() {
            Person p = new Person { Name = "toto", Age = 5};
            Type tPerson = p.GetType();
            PropertyInfo[] props = tPerson.GetProperties();
            StringBuilder builder = new StringBuilder(30);
            foreach (PropertyInfo prop in props)
            {
                builder.Append(prop.Name + "=" + prop.GetValue(p) + "\n");
            }

            builder.Append("------------------------------- \n");
            FieldInfo[] fields = tPerson.GetFields();
            foreach(FieldInfo f in fields){
                builder.Append(f.Name + "=" + f.GetValue(p) + "\n");
            }
            Console.WriteLine(builder);
        }
Type类的示例2:
        static void TypeTest2()
        {
            Person p = new Person();
            Type tPerson = p.GetType();
            PropertyInfo f = tPerson.GetProperty("Name");
            f.SetValue(p, "titi");
            Console.WriteLine(p.Name);

        }
Type类的示例3:
        static void TypeTest3()
        {
            Person p = new Person();
            Type tPerson = p.GetType();
            //所有的public方法
            MethodInfo[] methodsPublic = tPerson.GetMethods();
            //为了获取返回值,必须指定 BindingFlags.Instance 或 BindingFlags.Static。
            //public 的 static 的方法
            MethodInfo[] methodsPublicStatic = tPerson.GetMethods(BindingFlags.Public | BindingFlags.Static);
            //public 的 实例方法
            MethodInfo[] methodsPublicInstance = tPerson.GetMethods(BindingFlags.Public | BindingFlags.Instance);
            //只是在类中定义的(不包括继承的)public的 实例的 和static的 方法
            MethodInfo[] methodsPublicDeclareOnly = tPerson.GetMethods(BindingFlags.Public | BindingFlags.Instance| BindingFlags.DeclaredOnly | BindingFlags.Static);
        }

Type类示例4:
        static void TypeTest4()
        {
            Person person = new Person();
            Type tPerson = person.GetType();
            //得到所有的共有构造函数
            ConstructorInfo[] constructors = tPerson.GetConstructors();
            foreach (var ctor in constructors)
            {
                //Name都是 .ctor
                Console.WriteLine(ctor.Name);
                //构造函数的参数个数
                Console.WriteLine(ctor.GetParameters().Length);
                ParameterInfo[] parametres = ctor.GetParameters();
                foreach (var p in parametres)
                {
                    Console.WriteLine(p.Name + " : " + p.ParameterType);
                }
            }

            Person person1, person2;

            // 调用指定参数的 构造函数 创建对象
            ConstructorInfo c1 = tPerson.GetConstructor(new Type[2] { typeof(String), typeof(int) });
            if(c1 != null){
                //通过构造函数的invoke方法来创建对象, 创建出来的对象 是invoke方法的返回值(object类型)
                 person1 = c1.Invoke(new object[] { "toto", 2 }) as Person;
            }

            // 调用无参数的构造函数 创建对象
            ConstructorInfo c2 = tPerson.GetConstructor(new Type[0]);
            if (c2 != null)
            {
                 person2 = c2.Invoke(null) as Person;
            }

            // 没有一个参数为String的构造函数, 返回null  ==>  c3为null
            ConstructorInfo c3 = tPerson.GetConstructor(new Type[] { typeof(String)});
        }

通过反射调用类的私有成员

        static void TypeTest5() {
            Person person = new Person();
            Type tPerson = person.GetType();
            MethodInfo privateMethod =  tPerson.GetMethod("TestPrivateMethod", BindingFlags.Instance | BindingFlags.NonPublic);
            privateMethod.Invoke(person, null);
        }
类中的public private是针对compiler的, compiler负责语法检查。 运行时 是没有 public private限制的,只要代码到内存中,就可以调用执行。

5. 记事本程序实例

xmal代码:

        
            
                
                    
                    
                    
                
                
                    
                    
                    
               
                
            
        
    

c#代码:
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            AddPlugInsToMenu();
        }

        //读取程序集文件,并生成 插件按钮
        private void AddPlugInsToMenu() {
            // 获取正在运行的程序集 的 物理路径
            String assPath = this.GetType().Assembly.Location;
            // 获取 程序集 所有文件夹
            String assDirPath = System.IO.Path.GetDirectoryName(assPath);
            // 插件文件夹的路径
            String plugInDir = assDirPath + "\\plugs";
            // 扫描插件文件夹中的 所有 程序集文件名(获取所有dll文件)
            String[] dllFiles = System.IO.Directory.GetFiles(plugInDir, "*.dll");
            // 遍历程序集文件,并加载程序集文件到内存
            foreach(String strDll in dllFiles){
                // 根据程序集路径 加载程序集 到内存
                Assembly ass = Assembly.LoadFrom(strDll);
                // 判断程序集中是否有插件类
                    // 获取程序集中的public类
                Type[] types = ass.GetExportedTypes();
                Type notebookInterfaceType = typeof(MyNoteBookPlugInterfaceProject.IPlugIn);
                // 判断是否实现了记事本接口
                foreach(Type t in types){
                    // 判断t是否实现了 notebookInterfaceType 接口
                    if (t.IsAssignableFrom(notebookInterfaceType)) {
                        // 根据插件类,创建menuitem,并添加到menu中
                        PlugInMenuItem.Items.Add(new MenuItem { Header = t.Name });
                    }
                }

                //// 创建menuitem,并添加到menu中
                //PlugInMenuItem.Items.Add(new MenuItem { Header = strDll});
            }
        }
另一个记事本公司开发的dll中, 有一个接口, 在wpf程序中add ref这个程序集。其他开发plugin的公司,也必须add ref这个程序集。
namespace MyNoteBookPlugInterfaceProject
{
    public interface IPlugIn
    {
        String ProcessText(String text);
    }
}

开发plugin的公司:写一个class library project, 也add ref记事本公司开发的接口程序集。写实现了这个接口的插件类。build这个程序集,把dll放到记事本项目的plugInDir目录下。这个plugin就可以使用了。
namespace NoteBookPlugIn
{
    public class PlugToUpper : MyNoteBookPlugInterfaceProject.IPlugIn
    {

        public string ProcessText(string text)
        {
            return text.ToUpper();
        }
    }
}
namespace NoteBookPlugIn
{
    public class PlugToLower : IPlugIn
    {
        public string ProcessText(string text)
        {
            return text.ToLower();
        }
    }
}

运行结果:C# Reflection笔记_第3张图片

6. 动态创建对象

1. 通过Activator创建
object o = Activator.CreateInstance(Type type);
会调用无参数的构造函创建一个对象,如果没有无参数的构造函数,异常。
2. 通过 构造器 创建(Type类示例4




你可能感兴趣的:(C#)