今天也要做這個功能,就百度一下,結果搜索到了自己的文章。一開始還沒注意,當看到里面的一個注釋的方法時,一開始還以為自己復制錯了代碼,結果仔細一看網頁的文章,我去,原來是自己寫的,寫的確實不咋地。
把自己的文章的代碼復制了下來,稍微改了一下,運行看了一下效果,然后仔細一看,計算的總時間不對,如下圖:
上一篇文章的地址:https://www.cnblogs.com/guxingy/p/10142242.html
改了幾個地方:
/// <summary> /// 攔截器 /// </summary> public class CallingLogInterceptor : IInterceptor { private DateTime dt { get; set; } private TimeSpan ts { get; set; } /// <summary> /// 方法執行前 /// </summary> /// <param name="invocation"></param> private void PreProceed(IInvocation invocation) { dt = DateTime.Now; } /// <summary> /// 方法執行后 /// </summary> /// <param name="invocation"></param> private void PostProceed(IInvocation invocation) { ts = DateTime.Now - dt; Console.WriteLine($"方法名稱:{invocation.Method.Name}" ); Console.WriteLine($"開始時間:{dt.ToString("yyyy-MM-dd HH:mm:ss fff")}"); Console.WriteLine($"結束時間:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff")}"); Console.WriteLine($"所用時間:{ts.TotalSeconds}"); MethodOperationInfo.Add(invocation, ts.TotalMilliseconds); } /// <summary> /// 攔截 /// </summary> /// <param name="invocation"></param> public void Intercept(IInvocation invocation) { this.PreProceed(invocation); invocation.Proceed();//調用 this.PostProceed(invocation); } }
/// <summary> /// 測試類1 /// </summary> public class Class5_test1 { public virtual void test1() { test4(); test3(); test2(); } public virtual void test2() { System.Threading.Thread.Sleep(1000); } public virtual void test3() { System.Threading.Thread.Sleep(2000); } public virtual void test4() { System.Threading.Thread.Sleep(3000); } }
public class MyProxyGenerator { /// <summary> /// 創建一個代理對象 /// </summary> /// <typeparam name="T"></typeparam> /// <returns></returns> public static T CreateProxy<T>() where T : class { ProxyGenerator generator = new ProxyGenerator();//代理 CallingLogInterceptor interceptor = new CallingLogInterceptor();//定義 攔截器 T entity = generator.CreateClassProxy<T>(interceptor); return entity; } }
public class Class2 { public static void test1() { Class5_test1 entity = MyProxyGenerator.CreateProxy<Class5_test1>(); entity.test1(); MethodOperationInfo.ShowContainsDetail(); Console.Read(); } }
說明:所有的測試,只是修改了一下Class5_test1 這個類給
測試1
/// <summary> /// 測試類1 /// </summary> public class Class5_test1 { public virtual void test1() { test2(); test3(); test4(); } public virtual void test2() { System.Threading.Thread.Sleep(1000); } public virtual void test3() { System.Threading.Thread.Sleep(2000); } public virtual void test4() { System.Threading.Thread.Sleep(3000); } }

問題:這里test1調用了test2、test3、test4,那么執行test1所用時間,應該是test2、test3、test4所用時間的總和,正確所用時間至少應該是6秒(1+2+3),結果這里輸出的是3秒多,肯定不對。
測試2
/// <summary> /// 測試類1 /// </summary> public class Class5_test1 { public virtual void test1() { test4(); test3(); test2(); } public virtual void test2() { System.Threading.Thread.Sleep(1000); } public virtual void test3() { System.Threading.Thread.Sleep(2000); } public virtual void test4() { System.Threading.Thread.Sleep(3000); } }

問題:這里的test1方法的所用時間是1秒多,更不對了。
結論:
測試1 和 測試2 的區別,只是在test1里面改變了test2、test3、test4的執行順序,結果時間卻迥然不同,更奇怪的是,test1的所用時間,更是莫名的接近調用的倒數第二個方法。
測試1里面,test1的所用時間,很接近test4。
測試2里面,test1的所用時間,很接近test2。
反正就是所用時間,很接近 方法體里面 最后一個 調用的方法,原因找到了再補充,了解一下動態代理的原理或許就知道了,也獲取是自己寫錯了,估計是第一種可能
乘着自己好奇,又測試了一下,看下面哦
/// <summary> /// 測試類1 /// </summary> public class Class5_test1 { public virtual void test1() { Console.WriteLine("開始執行test1"); test4(); test3(); test2(); Console.WriteLine("執行完成test1"); } public virtual void test2() { System.Threading.Thread.Sleep(1000); } public virtual void test3() { System.Threading.Thread.Sleep(2000); } public virtual void test4() { System.Threading.Thread.Sleep(3000); } }
主要是是在,test1方法里面添加了方法的執行情況, 輸出 開始執行 和 執行完成。

找到原因了:攔截器對象只創建了一個,所以dt一直都是最后調用的那個方法的開始時間
還是自己寫的代碼有問題啊
賦上 改好的全部代碼:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Castle.Core.Interceptor; using Castle.DynamicProxy; using System.Reflection; using Newtonsoft.Json; using System.Security.Cryptography; namespace ConsoleApplication3 { //原文:https://www.cnblogs.com/guxingy/p/10142242.html public class Class2 { public static void test1() { Class5_test1 entity = MyProxyGenerator.CreateProxy<Class5_test1>(); entity.test1(); MethodOperationInfo.ShowContainsDetail(); } } public class MyProxyGenerator { /// <summary> /// 創建一個代理對象 /// </summary> /// <typeparam name="T"></typeparam> /// <returns></returns> public static T CreateProxy<T>() where T : class { ProxyGenerator generator = new ProxyGenerator();//代理 CallingLogInterceptor interceptor = new CallingLogInterceptor();//定義 攔截器 T entity = generator.CreateClassProxy<T>(interceptor); return entity; } } /// <summary> /// 攔截器 /// </summary> public class CallingLogInterceptor : IInterceptor { private DateTime dt { get; set; } private TimeSpan ts { get; set; } /// <summary> /// 方法執行前 /// </summary> /// <param name="invocation"></param> private void PreProceed(IInvocation invocation) { //創建一個唯一的標識 //創建一個對象,並記錄開始時間 //dt = DateTime.Now; //Console.WriteLine($"開始執行,方法名稱:{invocation.Method.Name},開始時間:{dt.ToString("yyyy-MM-dd HH:mm:ss fff")}"); //Console.WriteLine(); MethodOperationInfo.SetStartTime(invocation); } /// <summary> /// 方法執行后 /// </summary> /// <param name="invocation"></param> private void PostProceed(IInvocation invocation) { //通用標識找到之前創建的那個對象,並記錄結束時間 //ts = DateTime.Now - dt; //Console.WriteLine($"執行完成,方法名稱:{invocation.Method.Name}"); //Console.WriteLine($" 開始時間:{dt.ToString("yyyy-MM-dd HH:mm:ss fff")}"); //Console.WriteLine($" 結束時間:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff")}"); //Console.WriteLine($" 所用時間:{ts.TotalSeconds}"); //Console.WriteLine(); MethodOperationInfo.SetEndTime(invocation); } /// <summary> /// 攔截 /// </summary> /// <param name="invocation"></param> public void Intercept(IInvocation invocation) { this.PreProceed(invocation); invocation.Proceed();//調用 this.PostProceed(invocation); } } /// <summary> /// 擴展 /// </summary> public static class IntExtension { public static string ToString(this object obj, int len, bool afterFill = true) { string name = obj.ToString(); //int count = len - name.Length;//不能用這個 漢字和英文占用的長度不同 int count = len - System.Text.Encoding.Default.GetBytes(name).Length; if (afterFill) { for (int i = 0; i < count; i++) { name += " "; } return name; } else { string value = ""; for (int i = 0; i < count; i++) { value += " "; } value += name; return value; } } } /// <summary> /// 操作日志 /// </summary> public class MethodOperationInfo { public string NameSpaceName { get; set; } public string ClassName { get; set; } public string MethodName { get; set; } public string Parameters { get; set; } public string ParameterTypes { get; set; } public double TotalMilliseconds { get; set; } public string Name { get; set; } public DateTime StartTime { get; set; } public DateTime EndTime { get; set; } public bool IsEnd { get; set; }//標識這個對象是否完成計算耗時 /// <summary> /// 構造函數 /// </summary> /// <param name="invocation"></param> public MethodOperationInfo(IInvocation invocation) { StartTime = DateTime.Now; NameSpaceName = invocation.TargetType.Namespace; ClassName = invocation.TargetType.Name; MethodName = invocation.Method.Name;//方法名 ParameterTypes = GetParameterTypes(invocation);//參數類型 Parameters = GetParameter(invocation);//參數 Name = GetName(invocation); IsEnd = false; } /// <summary> /// 操作完成 /// </summary> /// <param name="invocation"></param> public void OperationFinish(IInvocation invocation) { EndTime = DateTime.Now; TotalMilliseconds = (EndTime - StartTime).TotalMilliseconds; IsEnd = true; } //存放 所有的 詳細信息 public static List<MethodOperationInfo> operationInfos = new List<MethodOperationInfo>(); /// <summary> /// 設置開始時間 /// </summary> /// <param name="invocation"></param> public static void SetStartTime(IInvocation invocation) { MethodOperationInfo model = new MethodOperationInfo(invocation); operationInfos.Add(model); } /// <summary> /// 設置結束時間 /// </summary> /// <param name="invocation"></param> public static void SetEndTime(IInvocation invocation) { MethodOperationInfo model = GetModel(invocation); model.OperationFinish(invocation); } /// <summary> /// 獲取一個對象 /// </summary> /// <param name="invocation"></param> /// <returns></returns> public static MethodOperationInfo GetModel(IInvocation invocation) { string name = GetName(invocation); return operationInfos.Where(c => (c.Name == name) && !c.IsEnd).FirstOrDefault(); } /// <summary> /// 獲取 這個方法 唯一的編號,針對方法而已 /// </summary> /// <param name="invocation"></param> /// <returns></returns> public static string GetName(IInvocation invocation) { string NameSpaceName = invocation.TargetType.Namespace; string ClassName = invocation.TargetType.Name; string MethodName = invocation.Method.Name;//方法名 string ParameterTypes = GetParameterTypes(invocation); string Parameters = GetParameter(invocation);//參數 string strId = $"{NameSpaceName},{ClassName},{MethodName},{ParameterTypes}"; //MD5加密 byte[] result = Encoding.Default.GetBytes(strId); MD5 md5 = new MD5CryptoServiceProvider(); byte[] output = md5.ComputeHash(result); return BitConverter.ToString(output).Replace("-", ""); } /// <summary> /// 獲取方法的參數類型 /// 如:(System.String, System.Object, System.Int32) /// </summary> /// <param name="invocation"></param> /// <returns></returns> public static string GetParameterTypes(IInvocation invocation) { MethodInfo mInfo = invocation.Method; ParameterInfo[] pInfos = mInfo.GetParameters(); string str = ""; str += "("; for (int j = 0; j < pInfos.Length; j++) { var p = pInfos[j]; string pTypeName = $"{p.ParameterType.ToString()}, "; if (p.ParameterType.IsGenericType && (p.ParameterType.GetGenericTypeDefinition() == typeof(Nullable<>))) { pTypeName = $"{Nullable.GetUnderlyingType(p.ParameterType).Name}?, "; } str += pTypeName; } str = str.TrimEnd(' ').TrimEnd(','); str += ")"; return str; } /// <summary> /// 獲取方法的參數 /// </summary> /// <param name="invocation"></param> /// <returns></returns> public static string GetParameter(IInvocation invocation) { string Parameters = "";//參數 if ((invocation.Arguments != null) && (invocation.Arguments.Length > 0)) { Parameters = JsonConvert.SerializeObject(invocation.Arguments); } return Parameters; } /// <summary> /// 顯示日志 /// </summary> /// <param name="ShowDetailRecord">是否顯示詳情記錄-比較占用時間</param> /// <param name="IsFilter">是否開啟過濾</param> public static void Show(bool ShowDetailRecord = true, bool IsFilter = false) { StringBuilder sb = new StringBuilder(); DateTime beforDT = DateTime.Now; List<string> list_Show_Method = new List<string>() { "GetSingle_Value1" };//可改為配置參數 //每個方法-耗時 string str = ""; double totalMilliseconds_AllMethod = 0; //去重 var operationInfos_NotRepeat = operationInfos.GroupBy(c => c.Name).Select(c => c.First()).ToList(); //分組統計 operationInfos.GroupBy(c => c.Name).Select(c => new { Name = c.Key, ExecuteNumber = c.Count(), TotalMilliseconds = c.Sum(a => a.TotalMilliseconds) }).ToList().ForEach(c => { var item = operationInfos_NotRepeat.Where(x => x.Name == c.Name).First(); totalMilliseconds_AllMethod += item.TotalMilliseconds; str += $"命名空間:{item.NameSpaceName.ToString(40)} "; str += $"類名:{item.ClassName.ToString(30)} "; str += $"方法:{item.MethodName.ToString(80)} "; str += $"次數:{c.ExecuteNumber.ToString(10)} "; str += $"耗時:{c.TotalMilliseconds.ToString(10, false) }毫秒 "; str += $"\r\n"; }); sb.Append(str + "\r\n\r\n"); //方法-總耗時 str = ""; str += $"所有方法-耗時:{totalMilliseconds_AllMethod}毫秒 "; str += $"{totalMilliseconds_AllMethod / 1000}秒 "; str += $"{(totalMilliseconds_AllMethod / 1000 / 60).ToString("f2")}分鍾 "; str += $"當前時間:{DateTime.Now} "; sb.Insert(0, str + "\r\n\r\n"); #region 方法 每次耗時的 詳情信息 if (ShowDetailRecord) { for (int i = 0; i < operationInfos.Count; i++) { Console.WriteLine($"處理數據-當前行:{operationInfos.Count - i}"); var item = operationInfos[i]; //數據過濾 if (IsFilter && !list_Show_Method.Contains(item.MethodName)) { continue; } sb.Append($"命名空間:{item.NameSpaceName.ToString(40)} "); sb.Append($"類名:{item.ClassName.ToString(30)} "); sb.Append($"方法:{item.MethodName + item.ParameterTypes.ToString(80)} "); sb.Append($"耗時:{item.TotalMilliseconds.ToString(10, false) }毫秒 "); sb.Append($"參數:{item.Parameters.ToString(50)} "); sb.Append($"\r\n"); } } #endregion //計算日志-耗時 sb.Insert(0, $"計算日志-耗時:{DateTime.Now.Subtract(beforDT).TotalSeconds.ToString("#0.00000")}秒 \r\n\r\n"); System.IO.File.WriteAllText($"LOG_{DateTime.Now.ToString("yyyyMMddHHmmssfff")}.txt", sb.ToString()); Console.WriteLine("完成!"); } /// <summary> /// 顯示日志 包含詳情數據 /// </summary> public static void ShowContainsDetail() { Show(true); } /// <summary> /// 顯示日志 不包含詳情數據 /// </summary> public static void ShowNotContainsDetail() { Show(false); } } /// <summary> /// 測試類1 /// </summary> public class Class5_test1 { public virtual void test1() { Console.WriteLine("主方法-test1-開始執行"); Console.WriteLine(); for (int i = 0; i < 4; i++) { test4(); } for (int i = 0; i < 3; i++) { test3(); } for (int i = 0; i < 2; i++) { test2(); } Console.WriteLine("主方法-test1-執行完成"); Console.WriteLine(); } public virtual void test2() { System.Threading.Thread.Sleep(200); } public virtual void test3() { System.Threading.Thread.Sleep(300); } public virtual void test4() { System.Threading.Thread.Sleep(400); } } }
但是當要代理的那個的類,如果沒有無參構造函數,這種方法就還是用不了,准備解決這個問題,解決了到時候再貼上解決方案。
本來是准備用到生產環境測試一下自己的代碼的,結果,我去,有參構造函數,接口,多態,麻蛋都有啊,改好了,再繼續貼代碼
