日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > C# >内容正文

C#

C# 在自定义的控制台输出重定向类中整合调用方信息

發布時間:2023/12/4 C# 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C# 在自定义的控制台输出重定向类中整合调用方信息 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

C# 在自定義的控制臺輸出重定向類中整合調用方信息

目錄

C# 在自定義的控制臺輸出重定向類中整合調用方信息

一、前言

二、輸出重定向基礎版

三、輸出重定向進階版(傳遞調用方信息)

四、后記及資源

獨立觀察員 2021 年 1 月 6 日

?

一、前言

眾所周知,在 .NET 的控制臺應用程序(就是那種小黑框程序)中輸出信息,使用的是控制臺輸出方法?Console.Write ("消息") 或?Console.WriteLine ("消息"),這兩個方法稱為標準輸出。而在 Winform、WPF、網頁程序中,使用這種方法輸出的信息是沒有地方顯示的,在這些程序中,我們一般把信息輸出到相應的顯示控件中,或者寫入日志中。

?

比如我這有個 Winform 測試程序,相關按鈕的后臺邏輯就是向控制臺輸出 “哈哈哈”,一般情況下,點擊這個按鈕,左邊的消息框將不會有任何消息輸出:

?

二、輸出重定向基礎版

但是這里卻能顯示出相關消息,是怎么回事呢?原來我在構造函數中添加了這么一句 —— Console.SetOut (new ConsoleWriter (ShowInfo)); ?—— 這就把原本輸出到控制臺的消息,重定向給了方法 ShowInfo 來進行輸出,而 ShowInfo 方法內通過設置文本框的文本內容來達到了顯示消息的效果:

?

其中的關鍵就是自定義類 ConsoleWriter(后面有新版):

using System; using System.IO; using System.Text; /** 代碼已托管 https://gitee.com/dlgcy/dotnetcodes/tree/dlgcy/DotNet.Utilities/ConsoleHelper*/ namespace DotNet.Utilities.ConsoleHelper {/// <summary>/// [dlgcy] Console 輸出重定向/// 其他版本:DotNet.Utilities.WinformHelper.TextBoxWriter/// 用法示例:/// 在構造器里加上:Console.SetOut (new ConsoleWriter (s => { LogHelper.Write (s); }));/// </summary>/// <example>/// <code>/// public class Example/// {/// public Example()/// {/// Console.SetOut(new ConsoleWriter(s => { LogHelper.Write(s); }));/// }/// }/// </code>/// </example>public class ConsoleWriter : TextWriter{private readonly Action<string> _Write;private readonly Action<string> _WriteLine;/// <summary>/// Console 輸出重定向/// </summary>/// <param name="write"> 日志方法委托(針對于 Write)</param>/// <param name="writeLine"> 日志方法委托(針對于 WriteLine)</param>public ConsoleWriter(Action<string> write, Action<string> writeLine){_Write = write;_WriteLine = writeLine;}/// <summary>/// Console 輸出重定向/// </summary>/// <param name="write"> 日志方法委托 </param>public ConsoleWriter(Action<string> write){_Write = write;_WriteLine = write;}// 使用 UTF-16 避免不必要的編碼轉換public override Encoding Encoding => Encoding.Unicode;// 最低限度需要重寫的方法public override void Write(string value){_Write(value);}// 為提高效率直接處理一行的輸出public override void WriteLine(string value){_WriteLine(value);}} }

?

主要就是重寫了 TextWriter 類的 Write 方法,然后在重寫的 Write 方法中調用外部設置好的(通過構造函數)相關委托方法進行實際的信息輸出。

?

以上就是之前的版本,工作地還不錯。不過,當我們想在記錄信息時同時記錄調用方的信息時,問題就來了。

?

三、輸出重定向進階版(傳遞調用方信息)

要記錄方法的調用方信息,我們很容易想到可以使用?C#5.0 中新增的獲取調用方信息的方式,話不多說,改造 ShowInfo 方法如下即可:

/// <summary> /// 顯示消息 /// </summary> private void ShowInfo(string info, [CallerFilePath] string filePath = "", [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0) {TBInfo.Text += $"[{DateTime.Now:HH:mm:ss.ffff}][{filePath}][{memberName}][{lineNumber}] {info}\r\n\r\n"; }//private void ShowInfo(string info) //{ // TBInfo.Text += $"[{DateTime.Now:HH:mm:ss.ffff}] {info}\r\n\r\n"; //}

?

可以看到方法新增了以 CallerFilePath、CallerMemberName、CallerLineNumber 三個特性標注的三個可選參數,這樣就能自動獲得調用方法者的 文件名、成員名、行號了。

?

自然,構造函數中的重定向方法也需要更改:

public FormTest() {InitializeComponent();//Console.SetOut(new ConsoleWriter(ShowInfo));Console.SetOut(new ConsoleWriter(msg => { ShowInfo(msg); })); }

?

運行結果如下:

?

表面上看好像信息都有了,但是定睛一看,怎么調用成員顯示的是 .ctor 而不是 BtnConsoleRedirect_Click ?行號顯示的是 18 而不是 69?其實這里顯示的信息是構造函數的(因為重定向語句在那里)。那么有沒有辦法顯示實際的調用位置呢?我們繼續改造。

?

這次改造的是重定向類 ConsoleWriter:

using System; using System.IO; using System.Text; /** 代碼已托管 https://gitee.com/dlgcy/dotnetcodes/tree/dlgcy/DotNet.Utilities/ConsoleHelper* 依賴:ClassHelper 類中獲取調用信息的方法。*/ namespace DotNet.Utilities.ConsoleHelper {/// <summary>/// [dlgcy] Console 輸出重定向/// 其他版本:DotNet.Utilities.WinformHelper.TextBoxWriter/// 用法示例:/// 在構造器里加上:Console.SetOut (new ConsoleWriter (s => { LogHelper.Write (s); }));/// </summary>/// <example>/// <code>/// public class Example/// {/// public Example()/// {/// Console.SetOut(new ConsoleWriter(s => { LogHelper.Write(s); }));/// }/// }/// </code>/// </example>public class ConsoleWriter : TextWriter{private readonly Action<string> _Write;private readonly Action<string> _WriteLine;private readonly Action<string, string, string, int> _WriteCallerInfo;/// <summary>/// Console 輸出重定向/// </summary>/// <param name="write"> 日志方法委托(針對于 Write)</param>/// <param name="writeLine"> 日志方法委托(針對于 WriteLine)</param>public ConsoleWriter(Action<string> write, Action<string> writeLine){_Write = write;_WriteLine = writeLine;}/// <summary>/// Console 輸出重定向/// </summary>/// <param name="write"> 日志方法委托 </param>public ConsoleWriter(Action<string> write){_Write = write;_WriteLine = write;}/// <summary>/// Console 輸出重定向(帶調用方信息)/// </summary>/// <param name="write"> 日志方法委托(后三個參數為 CallerFilePath、CallerMemberName、CallerLineNumber)</param>public ConsoleWriter(Action<string, string, string, int> write){_WriteCallerInfo = write;}/// <summary>/// 使用 UTF-16 避免不必要的編碼轉換/// </summary>public override Encoding Encoding => Encoding.Unicode;/// <summary>/// 最低限度需要重寫的方法/// </summary>/// <param name="value"> 消息 </param>public override void Write(string value){if (_WriteCallerInfo != null){WriteWithCallerInfo(value);return;}_Write(value);}/// <summary>/// 為提高效率直接處理一行的輸出/// </summary>/// <param name="value"> 消息 </param>public override void WriteLine(string value){if (_WriteCallerInfo != null){WriteWithCallerInfo(value);return;}_WriteLine(value);}/// <summary>/// 帶調用方信息進行寫消息/// </summary>/// <param name="value"> 消息 </param>private void WriteWithCallerInfo(string value){//3、System.Console.WriteLine -> 2、System.IO.TextWriter + SyncTextWriter.WriteLine -> 1、DotNet.Utilities.ConsoleHelper.ConsoleWriter.WriteLine -> 0、DotNet.Utilities.ConsoleHelper.ConsoleWriter.WriteWithCallerInfovar callInfo = ClassHelper.GetMethodInfo(4);_WriteCallerInfo(value, callInfo?.FileName, callInfo?.MethodName, callInfo?.LineNumber ?? 0);}} }

?

即新增一個包含了調用方信息三個參數的委托?_WriteCallerInfo,以及配套的構造方法,然后在 Write 方法中優先使用?_WriteCallerInfo 委托方法。另外,引入了一個獲取調用方信息的方法(改造自《C# 獲取當前方法信息,上端調用方方法信息以及方法調用鏈》):

using System; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.Serialization.Formatters.Binary; /** 代碼已托管 https://gitee.com/dlgcy/dotnetcodes/tree/dlgcy/DotNet.Utilities/Object*/ namespace DotNet.Utilities {public class ClassHelper{#region 調用信息/* 參考:https://blog.csdn.net/m0_37886901/article/details/105266848 *//// <summary>/// 獲取方法調用信息;/// </summary>/// <param name="index">0 是本身,1 是調用方,2 是調用方的調用方... 以此類推 </param>/// <returns>MethodInfo 對象 </returns>public static MethodInfo GetMethodInfo(int index){try{index++; // 由于這里是封裝了方法,相當于上端想要獲取本身,其實對于這里而言,上端的本身就是這里的上端,所以需要 + 1,以此類推var stack = new StackTrace(true);//0 是本身,1 是調用方,2 是調用方的調用方... 以此類推var currentFrame = stack.GetFrame(index);var method = currentFrame.GetMethod();var module = method.Module;var declaringType = method.DeclaringType;var stackFrames = stack.GetFrames();string callChain = string.Join(" -> ", stackFrames.Select((r, i) =>{if (i == 0) return null;var m = r.GetMethod();return $"{m.DeclaringType.FullName}.{m.Name}";}).Where(r => !string.IsNullOrWhiteSpace(r)).Reverse());return new MethodInfo(){Method = method,ModuleName = module.Name,Namespace = declaringType.Namespace,ClassName = declaringType.Name,FullClassName = declaringType.FullName,MethodName = method.Name,CallChain = callChain,LineNumber = currentFrame.GetFileLineNumber(),FileName = currentFrame.GetFileName(),};}catch (Exception ex){Console.WriteLine(ex);return null;}}/// <summary>/// 方法調用信息/// </summary>public class MethodInfo{/// <summary>/// 方法完整信息;/// </summary>public MethodBase Method { get; set; }/// <summary>/// 模塊名/// </summary>public string ModuleName { get; set; }/// <summary>/// 命名空間/// </summary>public string Namespace { get; set; }/// <summary>/// 類名/// </summary>public string ClassName { get; set; }/// <summary>/// 完整類名/// </summary>public string FullClassName { get; set; }/// <summary>/// 方法名/// </summary>public string MethodName { get; set; }/// <summary>/// 調用鏈/// </summary>public string CallChain { get; set; }/// <summary>/// 行號/// </summary>public int LineNumber { get; set; }/// <summary>/// 文件名/// </summary>public string FileName { get; set; }}#endregion} }

?

最后,恢復測試程序構造函數處的重定向語句為之前的寫法,自動識別為調用 ConsoleWriter 中我們新增的那個構造函數:

?

運行,測試,可以看到方法名和行號都對了:

?

四、后記及資源

這種重定向的方式個人覺得挺方便的,比如在動態庫中全都寫成輸出控制臺的方式,然后在主程序構造函數中指定重定向;另外,還可用于轉錄到日志:

?

上圖所示的日志方法參見:《『簡易日志』NuGet 日志包 SimpleLogger》

?

本文測試程序相關代碼:https://gitee.com/dlgcy/dotnetcodes/tree/dlgcy/DotNet.Utilities.Test

轉錄到日志的參考項目:https://gitee.com/dlgcy/WPFTemplate

?

總結

以上是生活随笔為你收集整理的C# 在自定义的控制台输出重定向类中整合调用方信息的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。