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# 在自定义的控制台输出重定向类中整合调用方信息相关推荐
- php打印函数console,PHP内置Web Server探究(2)自定义PHP控制台输出console函数
PHP内置Web Server探究(二)自定义PHP控制台输出console函数 我们在开发APP的服务器端,当和APP进行联调时通常需要实时跟踪URL请求和参数的接收情况. 但PHP并没有像Pyth ...
- 键盘录入多个数据,以0结束,要求在控制台输出所有数据中的最大值(ArrayList集合)
思路:将录入的多个数据存储到ArrayList集合中,创建长度为集合大小的数组,将集合转为数组,然后对数组排序,这样数组中最后一位就是所有数据中的最大值. 完整代码如下: import java.ut ...
- mql5的include库文件中自定义enum类型在指标文件中的调用方式
在mql5中编写指标,调用的include文件中某个类中使用了自定义ENUM枚举类型,如图所示: enum Smooth_Method { MODE_SMA_, //SMA MODE_EMA_, // ...
- python将控制台输出保存至文件
很多时候在Linux系统下运行python程序时,控制台会输出一些有用的信息.为了方便保存这些信息,有时需要对这些信息进行保存.这里介绍几种将控制台输出保存到文件中的方式: 1 重定向标准输出流 重定 ...
- python文件输出-python将控制台输出保存至文件的方法
很多时候在Linux系统下运行python程序时,控制台会输出一些有用的信息.为了方便保存这些信息,有时需要对这些信息进行保存.这里介绍几种将控制台输出保存到文件中的方式: 1 重定向标准输出流 重定 ...
- IntelliJ IDEA 自定义控制台输出多颜色格式功能 --- 安装Grep Console插件
IntelliJ IDEA 自定义控制台输出多颜色格式功能 1. 打开IDEA设置面板 2. 点击插件(Plugins)安装Grep Console 3. 重启IDEA后设置颜色 4. 效果图 5. ...
- springboot项目控制台输出自定义图案
如何在springboot项目控制台输出自定义图案呢? 本人的项目输出的是我名字的全拼,所以这里只介绍如何在控制台输出全拼名字. 1.http://patorjk.com/software/taag/ ...
- 将控制台输出重定向到textbox的dotnet类
//实现思想是使用windows api CreatePipe 创建一个匿名管道 //接收控制台命令的输出,并产生委托事件. //具体实现见以下代码: using System; using Syst ...
- springboot 控制台输出错误信息_springboot(6)——整合日志
概述 我们在平时开发项目的时候想知道程序运行情况一般可以使用sysout.print();打印一些关键的代码或者通过debug查看运行状态,但是对于这种sysout.print();很现任出现代码多余 ...
最新文章
- Hadoop入门进阶步步高(一)-环境准备
- 8.11zju集训日记
- matlab的灰色关联,灰色关联度Matlab代码
- c#扫描图片去黑边(扫描仪去黑边)
- CListControl的OnMouseMove和OnNcHitTest
- ExtJs 备忘录(1)—— Form表单(一) [ 控件使用 ]
- 小米11和vivox60买哪个?
- mut a:T 和a:mut T的区别
- 自然语言处理入门——RNN架构解析
- 电子工程师私藏的一个网站
- 新益华基层医疗系统使用方法_「热缩带」管道防腐新方向,聚乙烯热缩带安装使用方法...
- iOS 多媒体(一)音频播放
- 网络协议 终章 - GTP 协议:复杂的移动网络
- 设置无线网卡为AP工作模式(pi2和pi3)
- Java保留2位小数 JS保留2位小数 Java截取2位小数 Math.round((1.0/3)*100)/100.0
- 九宫格摆法_九宫格婚纱照摆法图片与技巧
- linux如何做动态壁纸实验报告,Ubuntu制作动态壁纸
- Git内部原理之深入解析环境变量
- Redis-学习笔记整理+汇总
- 反向代理方式实现IIS与Tomcat整合