分类
标签
.NET 9008 adb android apt asp.net ASP.NET Core audio bug C c++ C++ chrome cmd csharp CSharp css css3 debian debug dotnet dotnet Framework dpkg GDI&GDI+ gdi&gdi+ golang graphics html html5 http java javascript json kali linux linux mci microsoft minimap MSBuild mysql OpenCV PInvoke powershell python QQ rust shell speex sql tutorial ubuntu ui unity vb.net visual studio Visual Studio web Web win32 winapi windows winform WinForm wpf WPF xaml xfce 列表 刷机 前端 加密 反射 反编译 可视化 图像处理 多线程 字符串 安卓 实例 局域网 幻影坦克 库 开发语言 异步 微信 手册 手机 接口 摘要 救砖 数字签名 数字证书 数字音频 数据库 桌面程序 游戏 游戏引擎 源码 爬虫 玩游戏 电脑硬件 笔记 算法 类库 线性代数 编程语言 网络 脚本语言 计算机图形学 计算机基础 设计模式 语音编解码 运维 进制 面向对象编程 音频 音频编码解码
963 字
5 分钟
[C#] Stream 支持写入读取触发事件的类库 继承Stream基类
[C#] Stream 支持写入读取触发事件的类库
实现了 :
- 你可以将这个流类的实例提供给某些东西, 在它操作这个流时, 你可以通过事件来接收到消息, 并加以处理, 例如拒绝写入, 或在写入前判断写入的内容. 你可以稍微改动一下这个类以适应你的需求.
- 应用场景: 例如你使用了 IronPython 库, 并使用它执行了一些操作, 你希望 IronPython 每次 print 时, 你都能获取到内容, 则, 你可以使用这个触发流(TriggerStream)类, 将 IronPython 引擎的标准输出流设置为触发流的实例, 这样, 每当 IronPython 有内容输出时, 你都能获取到内容. 例如这个项目: 使用IronPython来制作一个支持py脚本操作内容的简易文本编辑器
- 应用场景: 例如, 你运行了一个 Process, 并且希望实时获取它的输出内容, 而不是运行完之后一次性获取所有内容, 同理, 你可以设置它的标准输出流为触发流实例, 然后每次它输出内容你都能接收到.
源代码 :
1. 首先是仅仅包含触发器, 而不会有任何存储行为的类
using System;
using System.IO;
namespace Null.Library.TriggerStream
{
class WriteStreamEventArgs : EventArgs
{
public byte[] Buffer;
public int Offset, Count;
public bool Denied = false;
}
class TriggerStream : Stream
{
public override bool CanRead => false;
public override bool CanSeek => false;
public override bool CanWrite => true;
public override long Length => 0;
public override long Position { get => 0; set { } }
public override void Flush() { }
public override int Read(byte[] buffer, int offset, int count)
{
return 0;
}
public override long Seek(long offset, SeekOrigin origin)
{
return 0;
}
public override void SetLength(long value) { }
public override void Write(byte[] buffer, int offset, int count)
{
if (PreviewWrite != null)
PreviewWrite.Invoke(this, new WriteStreamEventArgs()
{
Buffer = buffer,
Offset = offset,
Count = count,
});
}
public event EventHandler<WriteStreamEventArgs> PreviewWrite;
}
}
很明显能够看出, 上面的这个, 仅有事件触发, 而Seek, Read, Position等方法及属性, 都是直接使用的空或者返回合适的固定值.
2. 然后是带有存储功能的(使用MemoryStream)类
using System;
using System.IO;
namespace Null.Library.EventedStream
{
class ReadStreamEventArgs : EventArgs
{
public byte[] Buffer;
public int Offset, Count;
public bool Denied = false;
}
class WriteStreamEventArgs : EventArgs
{
public byte[] Buffer;
public int Offset, Count;
public bool Denied = false;
}
public class FlushStreamEventArgs : EventArgs
{
public bool Denied = false;
}
public class SetStreamLengthEventArgs : EventArgs
{
public long Value;
public bool Denied = false;
}
public class SeekStreamEventArgs : EventArgs
{
public long Offset;
public SeekOrigin SeekOrigin;
public bool Denied = false;
}
class EventedStream : Stream, IDisposable
{
MemoryStream baseMemory = new MemoryStream();
public override bool CanRead => baseMemory.CanRead;
public override bool CanSeek => baseMemory.CanSeek;
public override bool CanWrite => baseMemory.CanWrite;
public override long Length => baseMemory.Length;
public override long Position { get => baseMemory.Position; set => baseMemory.Position = value; }
public override void Flush()
{
if (PreviewFlush != null)
{
FlushStreamEventArgs args = new FlushStreamEventArgs();
PreviewFlush.Invoke(this, args);
if (args.Denied)
return;
}
baseMemory.Flush();
}
public override int Read(byte[] buffer, int offset, int count)
{
if (PreviewRead != null)
{
ReadStreamEventArgs args = new ReadStreamEventArgs()
{
Buffer = buffer,
Offset = offset,
Count = count
};
PreviewRead.Invoke(this, args);
if (args.Denied)
return 0;
}
return baseMemory.Read(buffer, offset, count);
}
public override long Seek(long offset, SeekOrigin origin)
{
if (PreviewSeek != null)
{
SeekStreamEventArgs args = new SeekStreamEventArgs()
{
Offset = offset,
SeekOrigin = origin
};
PreviewSeek.Invoke(this, args);
if (args.Denied)
return Position;
}
return baseMemory.Seek(offset, origin);
}
public override void SetLength(long value)
{
if (PreviewSetLength != null)
{
SetStreamLengthEventArgs args = new SetStreamLengthEventArgs()
{
Value = value
};
PreviewSetLength.Invoke(this, args);
if (args.Denied)
return;
}
baseMemory.SetLength(value);
}
public override void Write(byte[] buffer, int offset, int count)
{
if (PreviewWrite != null)
{
WriteStreamEventArgs args = new WriteStreamEventArgs()
{
Buffer = buffer,
Offset = offset,
Count = count
};
PreviewWrite.Invoke(this, args);
if (args.Denied)
return;
}
baseMemory.Write(buffer, offset, count);
}
public new void Dispose()
{
baseMemory.Dispose();
}
public event EventHandler<FlushStreamEventArgs> PreviewFlush;
public event EventHandler<SetStreamLengthEventArgs> PreviewSetLength;
public event EventHandler<SeekStreamEventArgs> PreviewSeek;
public event EventHandler<WriteStreamEventArgs> PreviewWrite;
public event EventHandler<ReadStreamEventArgs> PreviewRead;
}
}
原理 :
倒也简单, 直接继承基类, 然后实现方法即可.
提示 :
如果你要同时使用这两个,,, 别忘记稍微移动下事件参数使它们在同一个文件中, 并使两个事件流类using事件参数的命名空间, 否则, 在不完全指定命名空间的状况下, 会出现不明确引用的错误.
[C#] Stream 支持写入读取触发事件的类库 继承Stream基类
https://slimenull.com/posts/20210203030237/[项目实例] 使用 IronPython 库来创建一个支持使用Python脚本操作的简易文本编辑器
[踩坑记录] ASP.NET Core System.Data.SqlTypes.SqlNullValueException: 数据为空。不能对空值调用此方法