Сейчас читаю вот эту статью Difference between Buffer & Stream in C# и хотел бы для себя сначала уяснить что правильно понимаю, что происходит под капотом при выполнении инструкции FileStream stream = new FileStream("filepath.txt", FileMode.open).
На HDD/SSD лежит некий файл filepath.txt, который может быть размером даже больше RAM. И при выполнении вышеприведенной инструкции, исполняющая среда как-бы подносит к началу этого файла шланг и открывает краник. Водичка (байты) сразу не течет, а просто на готове.
Правильно ли я все описал?
Грубо говоря, поток - это место, откуда можно читать или куда модно писать данные. Это, если можно так выразиться, абстрактное понятие. Вот, например, мы создаем поток для доступа к файлу
using(var fileStream = File.OpenRead(path))
{
....
}
В примере создается экземпляр класса FileStream, который наследован от Stream. Это позволяет нам читать и писать в файл.
Тип MemoryStream - представляет собой поток чтения/записи в память. NetworkStream - сеть. И так далее.
Итак, до этого момента мы говорили о потоке, который является источником и/или приемником данных. Но, помимо таких потоков, есть потоки - декораторы. Декораторы сами по себе не являются источниками или приемноками данных. Они принимают поток как параметр и используют его для операций чтения и записи. Для чего они нужны? Читаем далее.
Далее, BufferedStream - это декоратор над Stream. Он позволяет буферизировать запись/чтение в поток. Это полезно при файловом вводе/выводе, так как чтение/запись с использованием буфера уменьшает количество записей/считываний, что увеличивает производительность. Его можно использовать с любым потоком, например
using(var bufferedFileStream =
new BufferedStream(File.OpenRead(path), 1024*1024)) // буфер в мегабайт
{
....
}
Как я сказал, BufferedStream - это декоратор, то есть он только расширяет возможности декорируемого потока. Вот пример другого декоратора - я его использовал для мониторинга операций чтения/записи
public class StreamSpy : Stream
{
private readonly Stream _inner;
private readonly ILog _log;
public StreamSpy(Stream inner, ILog log)
{
_inner = inner;
_log = log;
}
public override void Flush()
{
_inner.Flush();
_log.Debug("Flush!");
}
public override long Seek(long offset, SeekOrigin origin)
{
return _inner.Seek(offset, origin);
}
public override void SetLength(long value)
{
_inner.SetLength(value);
}
public override int Read(byte[] buffer, int offset, int count)
{
_log.Debug($"READ buffer {buffer.Length}, offset {offset}, count {count}, POSITION: {Position}");
return _inner.Read(buffer, offset, count);
}
public override void Write(byte[] buffer, int offset, int count)
{
_inner.Write(buffer, offset, count);
_log.Debug($"WRITE buffer {buffer.Length}, offset {offset}, count {count}");
}
public override bool CanRead => _inner.CanRead;
public override bool CanSeek => _inner.CanSeek;
public override bool CanWrite => _inner.CanWrite;
public override long Length => _inner.Length;
public override long Position
{
get => _inner.Position;
set => _inner.Position = value;
}
public override void Close()
{
base.Close();
_inner.Close();
_log.Info("CLOSE");
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
_inner.Dispose();
_log.Info("DISPOSE");
}
}
Так как это просто декораторы, то их можно объединять, например
using(var bufferedFileStream =
new BufferedStream(new StreamSpy(File.OpenRead(path), mylog), 1024*1024)) // буфер в мегабайт
{
....
}
теперь конечный поток bufferedFileStream представляет собой буферизированный поток чтения/записи в файл с логгированием операций.
Соответсвенно, если вы читаете байт используя просто FileStream - вы прочитаете ровно байт. Если вы читаете байт из файловго потока, который декорирован буфером, то вы либо читаете с диска весь буфер, либо читаете данные из буфера в памяти.
Апостиль в Лос-Анджелесе без лишних нервов и бумажной волокиты
Основные этапы разработки сайта для стоматологической клиники
Продвижение своими сайтами как стратегия роста и независимости