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

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/305929.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mac 上mysql怎么卸载不了_mac的mysql怎么卸载?

Mac下卸载mysql的方法&#xff1a;1、关闭mysql查看mysql是否启动&#xff1a;ps -ef |grep mysql输入&#xff1a;kill -9 (./mysqld前面第二个数字&#xff0c;这里是627) 然后回车&#xff0c;关闭mysql。2、卸载&#xff1a;在Mac终端使用下面的命令删除所有mysql文件即可s…

C#实现网页加载后将页面截取成长图片

背景最近再做一个需求&#xff0c;需要对网页生成预览图&#xff0c;如下图但是网页千千万&#xff0c;总不能一个个打开&#xff0c;截图吧&#xff1b;于是想着能不能使用代码来实现网页的截图。其实要实现这个功能&#xff0c;无非就是要么实现一个仿真浏览器&#xff0c;要…

“既然计划没有变化快,那制订计划还有个卵用啊!”

这是头哥侃码的第229篇原创每年年初&#xff0c;我的朋友圈里都会炸出不少在打完鸡血之后&#xff0c;迫不及待向全世界宣告自己 “新年Flag” 的人。有的人&#xff0c;把健身、养生设为目标&#xff0c;什么不暴瘦20斤不换头像呀&#xff0c;什么再也不吃炸鸡啤酒啦&#xff…

图书管理系统jsp代码_【程序源代码】使用Java开发的图书管理系统

关键字&#xff1a;java 管理系统 正文 | 内容01—【概述】使用Java开发的图书管理系统&#xff0c;读者可以注册登录&#xff0c;登录时会判断账号类型再分别跳到各自对应的页面&#xff0c;读者可以查找&#xff0c;借阅&#xff0c;还书&#xff0c;查看历史借阅记录&#x…

整合.NET WebAPI和 Vuejs——在.NET单体应用中使用 Vuejs 和 ElementUI

.NET简介.NET 是一种用于构建多种应用的免费开源开发平台&#xff0c;例如&#xff1a;Web 应用、Web API 和微服务云中的无服务器函数云原生应用移动应用桌面应用1). Windows WPF2). Windows 窗体3). 通用 Windows 平台 (UWP)游戏物联网 (IoT)机器学习控制台应用Windows 服务跨…

【gRPC】 在.Net core中使用gRPC

最近在学习.net core的微服务体系架构。微服务之间的通信常常通过gRPC进行同步通信&#xff0c;但是需要注意的是&#xff0c;大多数微服务之间的通信是通过事件总线进行异步通信。在微软介绍.net微服务体系架构的项目eShop中&#xff0c;微服务之间进行同步通信的场景很多&…

disconf mysql_Docker搭建disconf环境,三部曲之三:细说搭建过程

Docker下的disconf实战全文链接细说搭建过程在前两章中&#xff0c;我们利用远程或本地的镜像&#xff0c;快速体验了本地启动disconf的过程&#xff0c;本章我们一起来分析和梳理整个定制和搭建过程&#xff0c;了解这些后&#xff0c;我们就能根据自己的需要来定制本地的disc…

轻量级 Kubernetes K3s - Github热点

轻量级 Kubernetes k3sstar: 15.5kK3s是完全符合生产要求的Kubernetes发行版, 安装简单&#xff0c;可用于生产&#xff0c;整个二进制文件小于100M&#xff0c;作为单一文件打包部署&#xff0c;优势在于&#xff0c;你只需几秒钟就可以得到一个完全成熟的Kubernetes集群。htt…

java 固定长度队列_如何彻底搞懂 Java 数据结构?|CSDN 博文精选

作者 | 张振华.Jack责编 | 郭芮出品 | CSDN 博客本文和大家一起来重温《Java数据结构》经典之作。Java数据结构要理解Java数据结构&#xff0c;必须能清楚何为数据结构&#xff1f;数据结构&#xff1a;Data_Structure&#xff0c;它是储存数据的一种结构体&#xff0c;在此结构…

IdentityServer4 之 Resource Owner Password Credentials 其实有点尴尬

前言接着IdentityServer4的授权模式继续聊&#xff0c;这篇来说说 Resource Owner Password Credentials授权模式&#xff0c;这种模式在实际应用场景中使用的并不多&#xff0c;只怪其太开放啦&#xff0c;直接在客户端上拿着用户名和密码就去授权服务器获取AccessToken&#…

Xamarin使XRPC实现接口/委托远程调用

在之前的文章中已经介绍如何使用Beetlex.XRCP组件进行接口/委托远程调用&#xff1b;由于组件BeetleX.XRPC.Clients支持.NETStandard2&#xff0c;因此Xamarin同样可以使用它来实现基于接口/委托的数据交互通讯。接下来通过Xamarin实现一个简单的移动程序&#xff0c;并通过XRP…

mysql 拷贝安装_Mysql的安装和主从复制

安装mysql服务步骤一&#xff1a;首先下载mysql的yum源配置 &#xff0c;下载mysql的yum源wget http://dev.mysql.com/get/mysql57-community-release-el7-11.noarch.rpm步骤二&#xff1a;安装mysql的yum源yum -y install mysql57-community-release-el7-11.noarch.rpm步骤三&…

浅谈CLR基础知识

中午的时候&#xff0c;有一个小伙伴问我&#xff0c;CLR到底是进程还是线程&#xff0c;它和自己写的程序是怎么关联的。这一问&#xff0c;直接把我问懞了。下面我尝试用简单的语言来描述这个问题&#xff0c;有的地方无法讲的太细&#xff08;不然内容会太多&#xff09;&am…

Asp.Net Core使用Skywalking实现分布式链路追踪

介绍Skywalking 是 Apache 基金会下面的一个开源 APM 项目&#xff0c;是一套(APM)分布式追踪系统&#xff0c;提供了很多数据存储列如&#xff1a;Mysql&#xff0c;H2&#xff0c;Elasticsearch7 等。其中APM 全称是应用性能监测软件&#xff0c;主要是用来处理以及追踪分布式…

python 里什么时候缩进_python什么时候缩进

Python中的缩进(Indentation)决定了代码的作用域范围。这一点和传统的c/c有很大的不同(传统的c/c使用花括号花括号{}符决定作用域的范围&#xff1b;python使用缩进空格来表示作用域的范围&#xff0c;相同缩进行的代码是处于同一范围)。每行代码中开头的空格数(whitespace)用于…

C# 9 新特性 —— 补充篇

C# 9 新特性 —— 补充篇Intro前面我们分别介绍了一些 C# 9 中的新特性&#xff0c;还有一些我觉得需要了解一下的新特性&#xff0c;写一篇作为补充。Top-Level Statements在以往的代码里&#xff0c;一个应用程序必须要有 Main 方法才能运行&#xff0c;从 C# 9 开始&#xf…

使用Popup窗口创建无限级Web页菜单(5)

代码框架在(4)里面已经全部列出来了&#xff0c;现在工作就是按流程把他们完成。本来实现一个prototype的Menu菜单类只需要最多300行代码&#xff0c;可是后来做了一些操作习惯支持和UI显示上的优化后&#xff0c;代码猛增到了1000多行。不过final版本看起来确实比土不拉叽的pr…

【gRPC】ProtoBuf 语言快速学习指南

继上篇【gRPC】 在.Net core中使用gRPC了解了gRPC的使用&#xff0c;gRPC基于HTTP/2和ProtoBuf&#xff0c;ProtoBuf就非常有必要好好了解一下了&#xff0c;那么ProtoBuf究竟是什么&#xff1f;ProtoBuf Google Protocol Buffer是一种语言无关、平台无关、可扩展的序列化结构数…

vspythonqt混合_PYQT5 vscode联合操作qtdesigner的方法

除了使用pycharm外&#xff0c;还可使用vscode来操作pyqt&#xff0c;方法如下&#xff1a;1. 在vscode中配置相关的pyqt的相关根据自己实际情况修改第一项pyqt的路径2. 创建一个文件夹&#xff0c;右键&#xff0c;最后一项可以创建一个新窗口创建的新窗口后缀为 .ui右键中有以…

TIOBE 1 月榜单:Python年度语言四连冠,C 语言再次第一

喜欢就关注我们吧&#xff01;TIOBE 公布了 2021 年 1 月的编程语言排行榜。TIOBE 本月公布了 2020 年度编程语言&#xff0c;Python 获得四连冠&#xff0c;是过去一年中最受欢迎的编程语言。Python 在 2020 年实现了 2.01&#xff05; 的正增长&#xff1b;C 紧随其后&#x…