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下面,还是通过反射加载程序集,他们都很类似,所以,我们可以通过配置文件,将它们的程序集名字和类名放到配置文件里面,这样,可以不用修改代码,只改配置文件即可。
首先,在配置文件里创建一个appSettings节点,将程序集名字和类名放进去
<appSettings>
<add key="Person" value="Fhzm.dll|Fhzm.Person"/>
</appSettings>
然后读取配置文件,要先引入读取配置文件的程序集
然后根据配置文件的信息来创建对应的实例
//获取配置文件得字符串
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按钮)。
即在窗体中根据提供的类库数量动态生成按钮控件。
1、创建一个winform主项目(SelectUltraman)
只需要一个界面即可。项目结构如下图:
2、创建一个接口项目(Ultrman)
所有的插件都必须实现该接口,注意,接口的Name属性必须要有初始值,初始值必须要,根据初始值名字来判断是否调用这个类的方法
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类库
5、测试,将两个类库生成,将编译后的程序集文件复制到主项目的插件文件夹里面即可。文件属性要为始终复制
运行结果:
所以,只要提供实现了接口的类库,就可以动态添加插件进去,这样也是插件式开发交给用户,让用户自己取名字,只要用户提供类库就好
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构