需求:
在工作中遇到某一部分代碼是通過另一款軟件導出來的,在實際的項目部署中,這部分代碼會經常變動,那麼類名和函數名就會不確定,在覈心代碼部分就很難靈活應對。於是,利用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();
}
}
}