場景:
前不久,公司裡專案經理要求我實現流程處理,比如,使用者可以定義一個定時任務,每週一檢視報表。定時任務很簡單,用Quartz可以實現,但是使用者自己選擇報表就比較麻煩,因為系統的不同模組的生成報表的函式不一樣,於是有了這樣的想法:傳入一個方法名和方法的輸入引數,就可以呼叫該方法。
實現:
這裡主要用的是反射的方法。使用者要傳入方法名和方法引數,我們就需要先寫函式返回這些資訊,最後再包裝一下返回給使用者。
獲取某一程式集下所有類:(對我來說,獲取當前程式集下的類就夠了,要獲取其他程式集或dll的,請查詢其他資料)
public List<string> GetClass(string assembyName = null) { Assembly asm = Assembly.GetExecutingAssembly(); var allclass = asm.ExportedTypes.ToList(); return allclass.Select(u => u.Name).ToList(); }
獲取某一個類中所有的方法,主要用到GetMethods()的方法:
public List<string> GetMethod(Type type,string assembyName = null)
{
// 測試獲取指定類的方法,去除自帶的GetType方法
List<MethodInfo> methods = type.GetMethods().Where(u=>u.IsVirtual == false)
.Where(u => !u.Name.Contains("GetType")).ToList();
return methods.Select(u => u.Name).ToList();
}
獲取某一個方法的傳入引數型別,我是通過把method直接tostring(),來解析出輸入類的名稱的,應該有其他方法可以拿到輸入類的名稱:
public List<string> GetParameter(MethodInfo methodInfo, Assembly assembly) { // 由方法獲取輸入類,建立類 var method_FullName = methodInfo.ToString(); // 方法中找不到input類的資訊,只能從fullname中解析出來 Regex reg = new Regex(@"(?is)(?<=\()(.*)(?=\))"); Match m = reg.Match(method_FullName); string inputName = m.Value.Split(".").Last(); Type input_class = assembly.ExportedTypes.Where(u => u.Name.Contains(inputName)).First(); PropertyInfo[] propertyInfos = input_class.GetProperties(); List<string> result = new List<string>(); foreach ( var item in propertyInfos ) { var returnType = item.GetGetMethod().ReturnType; var name = returnType.ToString(); result.Add(name); } return result; }
可以獲取輸入類的屬性型別。
建立例項,並執行方法:
如果是用放射的方式例項化類,很麻煩的是類的建構函式中存在依賴關係。於是我才用依賴注入中獲取類的方法。
首先,建立一個IServiceProvider的靜態類來獲取系統依賴注入中的所有服務,再main函式中進行獲取和賦值:
public static class ServiceHelper { public static IServiceProvider Instance { get; set; } }
在main中獲取依賴注入:
public static void Main(string[] args) { var webhost = CreateHostBuilder(args).Build(); using ( var scope = webhost.Services.CreateScope() ) { ServiceHelper.Instance = webhost.Services; } webhost.Run(); }
這樣,就可以避免依賴關係,直接拿依賴注入中的類來反射方法,並執行:
// 獲取依賴注入中類 var api = ServiceHelper.Instance.GetService(do_class);
這裡的do_class是一個Type。
最後,是使用invoke來執行方法:
PropertyInfo[] propertyInfos = input_class.GetProperties();
Object Args = asm.CreateInstance(input_class.FullName);
foreach ( var item in propertyInfos )
{
var returnType = item.GetGetMethod().ReturnType;
var name = returnType.ToString();
// 匹配型別
if ( name.ToLower().Contains("string") )
{
item.SetValue(Args, "test");
}
else if ( name.ToLower().Contains("int") )
{
item.SetValue(Args, 1);
}
}
// 模擬某方法進行執行 object[] inputArgs = new object[] { Args }; try { var result = method.Invoke(api, inputArgs); } catch ( Exception ex) { Console.WriteLine(ex.Message); }
這裡的Args是通過input類獲取的引數型別而設定的例項物件,由於invoke的引數型別為objetc[],所以需要再包裝一層。
method是要呼叫的方法,api是該類的例項,從依賴注入中獲取。
最後:
最後就是Quartz的配合,這裡就不寫了,可以通過JobDataMap來傳遞引數。再結合HTTP client,可以實現基本流程處理。既可以呼叫內部函式,也可以http呼叫介面。