C# - Ôn lại để hiểu sâu hơn: Stream

Stream là gì?

Trong MSDN, khái niệm Stream được định nghĩa một cách ngắn gọn: "cung cấp một cái nhìn tổng quan về byte sequence". Tuy nhiên, khi hiểu một cách trực quan hơn, Stream có thể được ví như một dòng sông chảy trong hệ thống. Mỗi byte có thể được xem như một hạt nước trong dòng sông này.

Byte sequence là gì?

Byte sequence là một dãy các byte được sắp xếp theo thứ tự liên tục. Mỗi byte có thể được biểu diễn dưới dạng 8 bit nhị phân. Trong dòng sông (stream), mỗi byte sẽ có một vị trí xác định.

Stream và các phương thức, thuộc tính quan trọng

Stream là một lớp trừu tượng, do đó nó không thể được khởi tạo trực tiếp:

Stream stream = new Stream(); // Không được phép

Để sử dụng Stream, chúng ta cần thừa kế từ nó và định nghĩa lại các phương thức trừu tượng:

public class MyStreamExample : Stream
{
    public override bool CanRead
    {
        get { throw new NotImplementedException(); }
    }

    public override bool CanSeek
    {
        get { throw new NotImplementedException(); }
    }

    public override bool CanWrite
    {
        get { throw new NotImplementedException(); }
    }

    public override void Flush()
    {
        throw new NotImplementedException();
    }

    public override long Length
    {
        get { throw new NotImplementedException(); }
    }

    public override long Position
    {
        get
        {
            throw new NotImplementedException();
        }
        set
        {
            throw new NotImplementedException();
        }
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        throw new NotImplementedException();
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        throw new NotImplementedException();
    }

    public override void SetLength(long value)
    {
        throw new NotImplementedException();
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        throw new NotImplementedException();
    }
}

Phương thức Flush()

Phương thức này được sử dụng để xả các dữ liệu từ bộ đệm ra khỏi Stream. Khi viết dữ liệu vào tệp tin, dữ liệu sẽ được lưu vào bộ đệm trước tiên và chỉ đượcflush ra khỏi bộ đệm khi gọi phương thức này hoặc khi đóng stream.

Thuộc tính Position

Position cho biết vị trí hiện tại của con trỏ trong Stream. Điều quan trọng là phải Reset Position về 0 trước khi đọc dữ liệu từ đầu stream.

Phương thức Seek()

Seek được sử dụng để thay đổi vị trí của con trỏ trong Stream:

  • Seek(-3, SeekOrigin.End): Di chuyển con trỏ về vị trí cách cuối stream 3 byte.
  • Seek(0, SeekOrigin.Begin): Đặt con trỏ về đầu stream.
  • Seek(3, SeekOrigin.Current): Di chuyển con trỏ về vị trí cách hiện tại 3 byte.

Ví dụ sử dụng Stream:

static void Main(string[] args)
{
    byte[] bufer = null;
    string chuoiTest = "Stream!Hello world";
    char[] mangCharDoc = null;
    byte[] buferDoc = null;
    string ketQuaDoc = string.Empty;

    using (MemoryStream stream = new MemoryStream())
    {
        Console.WriteLine("Chuỗi ban đầu: {0}", chuoiTest);

        if (stream.CanWrite)
        {
            bufer = Encoding.Default.GetBytes(chuoiTest);
            stream.Write(bufer, 0, 3);

            Console.WriteLine("Vị trí hiện tại của stream: {0}", stream.Position + 1);

            long viTriMoi = stream.CanSeek ? stream.Seek(3, SeekOrigin.Current) : 0;
            Console.WriteLine("Vị trí mới sau khi seek: {0}", viTriMoi + 1);

            if (viTriMoi < bufer.Length)
            {
                stream.Write(bufer, (int)viTriMoi, bufer.Length - (int)viTriMoi);
            }

            stream.Position = 0;
            buferDoc = new byte[stream.Length];
            int soByteDoc = stream.CanRead ? stream.Read(buferDoc, 0, buferDoc.Length) : 0;

            int soChar = Encoding.Default.GetCharCount(buferDoc, 0, soByteDoc);
            mangCharDoc = new char[soChar];
            Encoding.Default.GetDecoder().GetChars(buferDoc, 0, soByteDoc, mangCharDoc, 0);

            foreach (char c in mangCharDoc)
            {
                ketQuaDoc += c;
            }

            Console.WriteLine("Kết quả đọc từ stream: {0}", ketQuaDoc);
        }

        stream.Close();
    }

    Console.ReadLine();
}

Thực hiện đọc và viết asynchronous:

Stream cung cấp các phương thức sau để thực hiện các thao tác asynchronous:

  • BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
  • BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
  • EndRead(IAsyncResult asyncResult)
  • EndWrite(IAsyncResult asyncResult)

Phương thức BeginRead và BeginWrite trả về một đối tượng IAsyncResult. Mỗi lần gọi BeginRead/BeginWrite phải gọi tương ứng EndRead/EndWrite để hoàn thành thao tác asynchronous.

Khi sử dụng asynchronous, các thao tác đọc và viết sẽ không chặn (non-blocking), giúp tăng hiệu năng trong các ứng dụng yêu cầu xử lý dữ liệu lớn hoặc các tác vụ phức tạp.

Thẻ: C# Stream Byte Async MemoryStream

Đăng vào ngày 12 tháng 7 lúc 03:48