实现步骤:
导入using System.Reflection;
Assembly.Load(“程序集”)加载程序集,返回类型是一个Assembly
foreach (Type type in assembly.GetTypes())
{
string t = type.Name;
}
得到程序集中所有类的名称
Type type = assembly.GetType(“程序集.类名”);获取当前类的类型
Activator.CreateInstance(type); 创建此类型实例
MethodInfo mInfo = type.GetMethod(“方法名”);获取当前方法
mInfo.Invoke(null,方法参数);
知识点1—Assembly类
1)要引用的程序集和需要程序集的程序在同一目录时,不需要加载程序集,直接可以获取其中的类型.
2)要引用的程序集合需要程序集不在同一目录中时,先要加载程序集,才能获取其中的类和类的方法等。
加载程序集
Assembly assembly = Assembly.LoadFrom("Graphic.dll");//加载程序集(cs文件生成.dll文件,.dll文件是一个程序集)
Console.WriteLine("name of assembley:"+assembly.GetName());//输出程序集的Name
根据类名创建类型的一个实例
object myobj =assembly.CreateInstance(nsp+classname,true, BindingFlags.Default, null, ars, null, null);
知识点2—Type类(获得实例对象的成员信息)
Type mytype = myobj.GetType();
if (myobj!=null)
Console.WriteLine("<{0}>类名:{1}",classList.Count,mytype.Name);
MethodInfo[] ms = mytype.GetMethods();
Console.WriteLine(" {0}的方法个数:{1}", mytype.FullName, ms.Length);
for (int i=0;i<ms.Length;i++)
Console.WriteLine(" ({0}){1}", i+1,ms[i].Name);
FieldInfo[] fs = mytype.GetFields();
Console.WriteLine(" {0}的字段个数:{1}", mytype.FullName, fs.Length);
for (int i = 0; i < fs.Length; i++)
Console.WriteLine(" ({0}){1}", i+1, fs[i].Name);
知识点3—调用方法
[方法名].Invoke([实例对象], [实参])
知识点4—为什么要用反射?
反射+配置文件
看过大话设计模式的,都知道在抽象工厂模式中.小菜在大鸟的指点下,用简单工厂,再用反射,再用配置文件,一步步将抽象工厂改装, 使得抽象工厂的缺点和个各类之间的耦合性都大大降低.
在不用反射+配置文件时,抽象工厂的问题是:
*如果需要增加多个数据库,就要增加多个类,更麻烦。
在使用反射+配置文件后,抽象工厂的改变是:
*如果需要更换数据库,不需改变代码,只需要修改配置文件中DB的值。
小结:反射技术的使用去除了switch或if,解除分支判断的耦合。在这里,反射可以说是简化了工厂的实现。
解决问题
假设我们有以下的泛型方法
public T fun<T>(){
return obj = new T();
}
如果T有很多,那通常的方法是使用swich进行判断
var className = "string";
var obj = new object();
swich(className){
case "string":
obj =fun<string>();
break;
case "int":
obj =fun<int>();
break;
...
}
解决方案
上面的写法会导致大量的冗余代码。
我们想要如下的解决方案,通过传入类的名字来动态的调用fun函数
//错误的方案
public object ExcuteMethod(string className){
var type = typeof(className);
return fun<type>();
}
泛型中的T是类型,而这里使用的type是参数。没有找到办法把类型当作参数传递。但是可以换一种思路,通过反射来直接执行函数。
以下是通过放射实现动态执行泛型函数
//获取当前程序集
Assembly mockAssembly = Assembly.GetExecutingAssembly();
//可以使用LoadFrom(URL)方法加载其他程序集(.dll文件),url是绝对路劲
Assembly mockAssembly = Assembly.LoadFrom("C:/test/test.dll");
//通过类名获取类,参数是类的全名,命名空间+类名,Myclass是泛型方法中fun<T>()中的T
var curType = mockAssembly.GetType("Myspacename.Myclass");
//获取方法所在类的Type,MethodClass是存放fun<T>()方法的类
var MethodType = typeof(MethodClass);
//如果是泛型类,如 MyGenericClass<T>{},要这样获得Type
var MethodType = typeof(MyGenericClass<>);
//获取泛型方法,
var GenericMethod =MethodType.GetMethod(MethodName);
//如果方法是多态的,即有多个同名方法,可以通过传入Type[]参数来找到方法
//这样可以找到方法fun(string para1,int para2)
var GenericMethod =MethodType.GetMethod(
MethodName, new Type[] {typeof(string),typeof(int)});
//合并生成最终的函数
MethodInfo curMethod =GenericMethod.MakeGenericMethod(curType);
//执行函数
//如果要执行的是静态函数,则第一个参数为null
//第二个参数为参数值
//即要调用的函数应该为 static fun<T>(string para1,int para2)
curMethod.Invoke(null, new object[]{"第一个参数的值",1});
//如果是非静态参数,第一个参数得传MethodClass的实例化对象
//即要调用的函数应该为 fun<T>(string para1,int para2)
var classobj = new MethodClass();
curMethod.Invoke(classobj, new object[]{"第一个参数的值",1});
使用缓存存储生成的方法
利用放射生成方法非常的耗费时间,所以可以考虑将生成的方法存在缓存中,以后直接执行就行,不用每次都重新生成方法
学习代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace learn
{
class Program
{
static void Main(string[] args)
{
string strClass = "learn.Test"; // 命名空间+类名
string strMethod = "Method"; // 方法名
Type type = Type.GetType(strClass); // 通过类名获取同名类
//方法一、使用静态类型
Object obj = System.Activator.CreateInstance(type); // 创建实例
MethodInfo method = type.GetMethod(strMethod, new Type[] { typeof(string), typeof(string) }); //获取方法
string r = (string)method.Invoke(obj, new object[] { "AAA", "BBBB" }); //执行方法
Console.WriteLine("Method 返回值:" + r);
//方法二、使用动态类型
//(dynamic 动态类型对象 b 来调用反射得到对象的属性、方法可直接调用,从而省去了频繁的类型转换操作)
dynamic dobj = System.Activator.CreateInstance(type);
string sr = dobj.Method("AAA", "BBBB");
Console.WriteLine("Method 返回值:" + sr);
Console.Read();
}
}
class Test
{
// 无参数,无返回值方法
public void Method()
{
Console.WriteLine("Method(无参数) 调用成功!");
}
// 有参数,无返回值方法
public void Method(string str)
{
Console.WriteLine("Method(有参数) 调用成功!参数 :" + str);
}
// 有参数,有返回值方法
public string Method(string str1, string str2)
{
Console.WriteLine("Method(有参数,有返回值) 调用成功!参数 :" + str1 + ", " + str2);
string className = this.GetType().FullName; // 非静态方法获取类名
return className;
}
}
}