# 原理

Console 有一个方法叫做 SetOut ,支持一个 TextWriter 的参数,调用之后,控制台的所有输出将不会经过标准输出流,而是经过自定义的流。
可以重定向本程序的控制台输出至文本流或其他部分。我写这个主要为了将 Blazor 的控制台输出显示到界面上,但是有个问题,无法获取文本颜色,现在也没什么解决方案。

# 实现

# 创建一个继承自 TextWriter 的类

  1. 继承 TextWriter
  2. 重写 Write(char)Write(string) 以及 WriteLine 方法,这样能够覆盖八成以上的场景,父类有更多的输出方法,按需重写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private class ObservableTextWriter : TextWriter
{
public override Encoding Encoding => Encoding.Default;

public override void Write(char value)
{

}

public override void Write(string value)
{

}

public override void WriteLine(string value)
{

}
}

# 暴露写入事件

外部监听事件即可获取控制台输出内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private class ObservableTextWriter : TextWriter
{
public override Encoding Encoding => Encoding.Default;

public event Action<string> OnWrite;

public override void Write(char value)
{
OnWrite?.Invoke(value.ToString());
}

public override void Write(string value)
{
OnWrite?.Invoke(value);
}

public override void WriteLine(string value)
{
OnWrite?.Invoke(value);
}
}

# 防抖

如果只是调用 Write(string)WriteLine 还好,我发现 Blazor 会调用很多 Write(char) ,导致每次控制台有输出时,都会卡死 UI,所以我使用防抖来降低向 UI 通知的速度。
防抖器的实现请参考另一篇文章

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
private class ObservableTextWriter : TextWriter
{
public override Encoding Encoding => Encoding.Default;

public event Action<string> OnWrite;

private Debouncer Debouncer { get; set; } = new();

private StringBuilder StringBuilder { get; set; } = new();

public override void Write(char value)
{
StringBuilder.Append(value);
Debouncer.Debounce(() =>
{
OnWrite?.Invoke(StringBuilder.ToString());
StringBuilder.Clear();
}, 500);
}

public override void Write(string value)
{
StringBuilder.Append(value);
Debouncer.Debounce(() =>
{
OnWrite?.Invoke(StringBuilder.ToString());
StringBuilder.Clear();
}, 500);
}

public override void WriteLine(string value)
{
StringBuilder.AppendLine(value);
Debouncer.Debounce(() =>
{
OnWrite?.Invoke(StringBuilder.ToString());
StringBuilder.Clear();
}, 500);
}
}