1. 程式人生 > 其它 >[C#] Stream 支援寫入讀取觸發事件的類庫 繼承Stream基類

[C#] Stream 支援寫入讀取觸發事件的類庫 繼承Stream基類

技術標籤:.NET類庫筆記c#

[C#] Stream 支援寫入讀取觸發事件的類庫

實現了 :

  1. 你可以將這個流類的例項提供給某些東西, 在它操作這個流時, 你可以通過事件來接收到訊息, 並加以處理, 例如拒絕寫入, 或在寫入前判斷寫入的內容. 你可以稍微改動一下這個類以適應你的需求.
  2. 應用場景: 例如你使用了 IronPython 庫, 並使用它執行了一些操作, 你希望 IronPython 每次 print 時, 你都能獲取到內容, 則, 你可以使用這個觸發流(TriggerStream)類, 將 IronPython 引擎的標準輸出流設定為觸發流的例項, 這樣, 每當 IronPython 有內容輸出時, 你都能獲取到內容. 例如這個專案:
    使用IronPython來製作一個支援py指令碼操作內容的簡易文字編輯器
  3. 應用場景: 例如, 你運行了一個 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事件引數的名稱空間, 否則, 在不完全指定名稱空間的狀況下, 會出現不明確引用的錯誤.