17反射基本使用三

一、反射实例化与多态结合

反射获取类型的成员

首先加载程序集

Assembly assembly = Assembly.LoadFile(@"C:\Users\Admin\source\repos\Test\ReflectDemo\Fhzm\bin\Debug\Fhzm.dll");


通过程序集获取类型

		//方式1,通过程序集获取该程序集下的所有类型
            //Type[] types= assembly.GetTypes();
            // Type type = types.First(m=>m.Name== "SuperMan");//从获得的所有类型里面筛选指定类型

            //方式2,从程序集里面拿到指定类型
            Type type = assembly.GetType("Fhzm.Person");

获取类型的所有属性

PropertyInfo[] propertyInfos = type.GetProperties();


获取类型的所有字段

FieldInfo[] fieldInfos = type.GetFields();

获取类型的所有方法

MethodInfo[] methodInfos = type.GetMethods();



反射创建类型的实例

首先加载程序集

Assembly assembly = Assembly.LoadFile(@"C:\Users\Admin\source\repos\Test\ReflectDemo\Fhzm\bin\Debug\Fhzm.dll");

实例化

  //方式1,从程序集里面拿到指定类型然后实例化
            //object o = assembly.CreateInstance("Fhzm.Person");//需要指明命名空间和类名
            dynamic dynamic = assembly.CreateInstance("Fhzm.Person");//使用dynamic关键字动态渲染,因为如果返回object类型实例,我们无法对Fhzm.Person类的属性赋值,因为object里面没有对应属性
            dynamic.Name = "张三";

            ////方式2,直接根据具体类型实例化
            //Type type = assembly.GetType("Fhzm.Person");
            //object obj=Activator.CreateInstance(type);//这样实例化需要具体类型
           

反射实例化与多态结合

上面实例化有个问题,如果创建object就无法对原类型的属性赋值,除非强转,不过强转的话为啥不直接实例化对应类型呢,所以这样又增加了耦合度了,但是如果用dynamic的话没有提示,属性名写错也不知道。所以我们可以尝试创建一个接口类库FhzmBaseInterface,让Fhzm.Person实现这个接口(IPerson)。这样调用实际上就降低了耦合性,因为根本没有引用Fhzm.Person这个类的引用。虽然这里有引用接口的引用,但是接口就是用来让类之间获取数据的,因为它本身是抽象的。

1、在接口类库FhzmBaseInterface里创建IPerson接口,将Fhzm.Person类的属性和方法抽象到这个接口里

public interface IPerson
    {
        string Name { get; set; }
        string Chla();
    }

2、在Fhzm.Person类里面实现接口即可

public class Person:IPerson
    {
        private string _name;
        public string Name { get { return _name; } set { _name = value; } }

        public string Chla()
        {
            return "我会吃喝拉撒";
        }

    }

3、在ReflectDemo.Program里面再次实例化就可以不用dynamic关键字了,而且在ReflectDemo.Program类里也没有添加Person类的引用,这样我们的主程序ReflectDemo和Fhzm类库就没有任何引用和联系了。指的是代码没有Fhzm类库的联系,反射使用Assembly.Load()方式的话也只需要将Fhzm.dll程序集拷贝到主项目的Debug下面而已,不需要动代码。

 IPerson person = (IPerson)assembly.CreateInstance("Fhzm.Person");//需要指明命名空间和类名
            person.Name = "张三";



二、通过配置文件拿到反射得程序集名字

继续用上面得项目,我们再创建一个类库Ace,在里面创建一个类也叫Ace类,让它也实现IPerson接口,然后编译后将该类库的dll文件(程序集)复制到主程序项目的Debug下面,方便反射获取该程序集

public class Ace : IPerson
    {
        private string _name;

        public string Name { get => _name; set => _name = value; }

        public string Chla()
        {
            return "我是Ace凹凸曼,我也会吃喝拉撒";
        }
    }

然后在主程序里分别用反射创建他们的实例去调用,我们可以发现,不管是拷贝他们各自的程序集文件到主程序项目的Debug下面,还是通过反射加载程序集,他们都很类似,所以,我们可以通过配置文件,将它们的程序集名字和类名放到配置文件里面,这样,可以不用修改代码,只改配置文件即可。

image

首先,在配置文件里创建一个appSettings节点,将程序集名字和类名放进去

<appSettings>
		<add key="Person" value="Fhzm.dll|Fhzm.Person"/>
	</appSettings>

然后读取配置文件,要先引入读取配置文件的程序集

image


然后根据配置文件的信息来创建对应的实例

 //获取配置文件得字符串
            string configurValue = ConfigurationManager.AppSettings["Person"];
            string assemblyName = configurValue.Split('|')[0];//程序集名字
            string className = configurValue.Split('|')[1];//类名


            //加载程序集
            Assembly assembly = Assembly.LoadFrom(assemblyName);//使用LoadFrom得去将对应项目得程序集手动放入本项目得Debug下面,或者右键添加引用
            IPerson person = (IPerson)assembly.CreateInstance(className);

这样,不需要修改代码,直接修改主程序项目文件里的Debug下的ReflectDemo.exe.config配置文件即可创建不同项目的实例,这样可以打开vs,将别的类库的程序集文件拖入该主程序项目的Debug下面,然后再修改配置文件即可实现创建别的类库的实例



三、反射之插件式Demo

创建一个插件式项目,只需要提供按钮名字的类库,即可动态添加插件(button按钮)。
即在窗体中根据提供的类库数量动态生成按钮控件。image

1、创建一个winform主项目(SelectUltraman)

只需要一个界面即可。项目结构如下图:
image

2、创建一个接口项目(Ultrman)

所有的插件都必须实现该接口,注意,接口的Name属性必须要有初始值,初始值必须要,根据初始值名字来判断是否调用这个类的方法
image

3、编写主项目后台

public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //获取所有程序集路径和名字
            string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "plugs");//直接反射获取类库程序集
            string[] filesName = Directory.GetFiles(path, "*.dll");

            foreach (var file in filesName)//循环所有程序集名字,然后根据程序集的类创建按钮
            {

                Assembly assembly = Assembly.LoadFile(file);
                Type[] types = assembly.GetExportedTypes();//通过程序集获取该程序集下面的所有公共类型(被public 修饰的类)
                Type type = types.First(m => !m.IsAbstract && typeof(IUltrman).IsAssignableFrom(m));//筛选非抽象类型(不要抽象类),并且这个类型必须是IUltrman类型的子类

                IUltrman ultrman = (IUltrman)Activator.CreateInstance(type);//根据类型实例化成一个IUltrman类型的对象


                //实例化一个按钮
                Button btn = new Button()
                {
                    Text = ultrman.UltrmanName,
                    Tag = ultrman.Name
                };

                //给按钮绑定单机事件
                btn.Click += btnSelectUltrman;

                //将按钮添加到控件中
                flowLayoutPanel1.Controls.Add(btn);
            }

        }

        //每个按钮的事件,过渡调用自己的Attack方法的方法
        private void btnSelectUltrman(object sender, EventArgs e)
        {
            string text = ((Button)sender).Text;
            string tag = ((Button)sender).Tag.ToString();
            label2.Text = GetUltrman(tag);
        }


        //底层直接调用调用自己的Attack方法
        private string GetUltrman(string tag)//获取Ultrman对象,用该对象调用对象里面的方法
        {
            string res = null;//返回值

            //获取所有程序集路径和名字
            string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "plugs");
            string[] filesName = Directory.GetFiles(path, "*.dll");

            //循环全部程序集的名字并根据完全限定名字加载程序集
            foreach (var item in filesName)
            {
                Assembly assembly = Assembly.LoadFile(item);
                Type[] types = assembly.GetExportedTypes();//通过程序集获取该程序集下面的所有公共类型(被public 修饰的类)
                Type type = types.First(m => !m.IsAbstract && typeof(IUltrman).IsAssignableFrom(m));//筛选非抽象类型(不要抽象类),并且这个类型必须是IUltrman类型的子类

                IUltrman ultrman = (IUltrman)Activator.CreateInstance(type);//根据类型实例化成一个IUltrman类型的对象
                if (ultrman.Name == tag)
                {
                    res = ultrman.Attack();
                }

            }
            return res;
        }


    }

4、建立两个类库测试,在里面建类,让其实现Ultrman接口

比如:Ace类库和Jack类库
image

5、测试,将两个类库生成,将编译后的程序集文件复制到主项目的插件文件夹里面即可。文件属性要为始终复制

image

运行结果:
image

所以,只要提供实现了接口的类库,就可以动态添加插件进去,这样也是插件式开发交给用户,让用户自己取名字,只要用户提供类库就好

posted @ 2021-12-24 12:29  青仙  阅读(59)  评论(0编辑  收藏  举报