需求:
在工作中遇到某一部分代码是通过另一款软件导出来的,在实际的项目部署中,这部分代码会经常变动,那么类名和函数名就会不确定,在核心代码部分就很难灵活应对。于是,利用C#的反射方法,实现了一个dll封装,可以把要执行的方法放到配置文件里,实现动态调用。
具体实现:
代码封装
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
namespace DllDynamicCall
{
public class DllCall
{
private Assembly _assembly;
public Dictionary<string,object> Objects { get; set; }
public Dictionary<string,Dictionary<string,MethodInfo>> Methods { get; set; }
/// <summary>
/// 构造器
/// </summary>
/// <param name="dllPath">DLL绝对路径</param>
public DllCall(string dllPath)
{
LoadDll(dllPath);
Objects=new Dictionary<string, object>();
Methods =new Dictionary<string, Dictionary<string, MethodInfo>>();
}
#region 公开方法
/// <summary>
/// 注册要目标类
/// </summary>
/// <param name="classFullName">类的完全限定名</param>
/// <param name="objectName">指定的对象名</param>
/// <param name="constructorParaTypes">构造器参数类型</param>
/// <param name="constructorParas">构造器参数</param>
public void RegisterObject(string classFullName,string objectName, Type[] constructorParaTypes,object[] constructorParas)
{
Type t = _assembly.GetType(classFullName, true, false);
ConstructorInfo constructorInfo = t.GetConstructor(constructorParaTypes);
if (constructorInfo != null)
{
object targetObject = constructorInfo.Invoke(constructorParas);
if (!Objects.ContainsKey(objectName))
{
Objects.Add(objectName,targetObject);
}
if (!Methods.ContainsKey(objectName))
{
Methods.Add(objectName, new Dictionary<string, MethodInfo>());
}
}
}
/// <summary>
/// 注册函数
/// </summary>
/// <param name="classFullName">类完全限定名</param>
/// <param name="objectName">制定函数所在对象名</param>
/// <param name="funcName">函数名</param>
public void RegisterFunc(string classFullName, string objectName,string funcName)
{
Type t = _assembly.GetType(classFullName, true, false);
MethodInfo method = t.GetMethod(funcName);
if (Methods.ContainsKey(objectName))
{
if (!Methods[objectName].ContainsKey(funcName))
{
Methods[objectName].Add(funcName,method);
}
}
}
/// <summary>
/// 调用函数
/// </summary>
/// <param name="objectName">目标对象名</param>
/// <param name="funcName">函数名</param>
/// <param name="paras">参数表,没有参数则用null</param>
/// <returns></returns>
public object CallFunc(string objectName, string funcName, object[] paras)
{
Dictionary<string,MethodInfo> targetObjec = Methods[objectName];
MethodInfo targetFunc = targetObjec[funcName];
var flag = BindingFlags.Public | BindingFlags.Instance;
object result = targetFunc. Invoke(Objects[objectName],flag,Type.DefaultBinder, paras,null);
return result;
}
#endregion
#region 私有方法
/// <summary>
/// 加载DLL
/// </summary>
/// <param name="dllPath">DLL绝对路径</param>
private void LoadDll(string dllPath)
{
if (File.Exists(dllPath))
{
_assembly = Assembly.LoadFrom(dllPath);
}
else
{
throw new FileNotFoundException($"{dllPath} isn't exist!");
}
}
#endregion
}
}
要调用的对象DLL
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TestDll
{
public class Person
{
private readonly int _a;
private readonly string _b;
public Person()
{
}
public Person(string racial,int weight)
{
this._a = weight;
this._b = racial;
}
public string ShowMessage(string name,int age)
{
return name + " is " + age + " years old.";
}
public string ShowFeature()
{
return _a + " " + _b;
}
}
}
测试代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Test
{
class Program
{
static void Main(string[] args)
{
// 根据绝对路径加载DLL
var dyDll = new DllDynamicCall.DllCall(@"E:\C#\UsageOfReflection\Test\bin\Debug\TestDll.dll");
// 注册类实例
dyDll.RegisterObject("TestDll.Person", "goodMan", new Type[] { typeof(string), typeof(int) }, new object[] { "亚洲", 180 });
// 注册特定实例的实例方法(命名空间TestDll,类Person,实例goodMan,方法ShowMessage)
dyDll.RegisterFunc("TestDll.Person", "goodMan", "ShowMessage");
// 注册特定实例的实例方法(命名空间TestDll,类Person,实例goodMan,ShowFeature)
dyDll.RegisterFunc("TestDll.Person", "goodMan", "ShowFeature");
// 调用方法
var message1 = (string)dyDll.CallFunc("goodMan", "ShowMessage", new object[] { "张三", 45 });
var message2 = (string)dyDll.CallFunc("goodMan", "ShowFeature", null);
Console.WriteLine(message1);
Console.WriteLine(message2);
Console.ReadKey();
}
}
}