Streams in C# provide a powerful and flexible way to read from and write to data sources, such as files, memory, network connections, and other I/O sources. The System.IO
namespace in .NET offers various stream classes that allow for efficient, controlled, and flexible data handling.
In this tutorial, we’ll explore streams, their different types, and how to use them effectively, along with examples, practical applications, and key takeaways.
A stream
in C# represents a sequence of bytes and serves as an abstraction for reading and writing data to different sources, such as files, network connections, and memory buffers. Streams help to manage data flow efficiently, making it easy to handle data from various sources without needing to know their specifics.
Streams are categorized based on the type of data source they operate on. Let’s examine some commonly used stream types in C#.
FileStream
is used to read from and write to files. It’s a convenient way to work with files on the file system.
using System;
using System.IO;
class Program
{
static void Main()
{
using (FileStream fs = new FileStream("example.txt", FileMode.OpenOrCreate))
{
byte[] content = System.Text.Encoding.UTF8.GetBytes("Hello, FileStream!");
fs.Write(content, 0, content.Length);
Console.WriteLine("Data written to file successfully.");
}
}
}
FileStream
is opened in OpenOrCreate
mode, meaning it will open an existing file or create a new one if it doesn’t exist.fs.Write
writes the byte
array to the file.MemoryStream
is useful for temporary storage, allowing data to be read and written to memory rather than disk, making it faster for short-term storage.
using System;
using System.IO;
class Program
{
static void Main()
{
using (MemoryStream ms = new MemoryStream())
{
byte[] content = System.Text.Encoding.UTF8.GetBytes("Hello, MemoryStream!");
ms.Write(content, 0, content.Length);
ms.Position = 0; // Reset position to the beginning
StreamReader reader = new StreamReader(ms);
Console.WriteLine("Read from memory: " + reader.ReadToEnd());
}
}
}
MemoryStream
, which operates in memory instead of on disk.StreamReader
.NetworkStream
facilitates data transmission over network connections, such as sockets. It’s commonly used for sending and receiving data across networks.
using System;
using System.Net.Sockets;
class Program
{
static void Main()
{
using (TcpClient client = new TcpClient("example.com", 80))
{
using (NetworkStream ns = client.GetStream())
{
byte[] data = System.Text.Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n");
ns.Write(data, 0, data.Length);
byte[] buffer = new byte[1024];
int bytesRead = ns.Read(buffer, 0, buffer.Length);
Console.WriteLine("Received: " + System.Text.Encoding.ASCII.GetString(buffer, 0, bytesRead));
}
}
}
}
TcpClient
is used to establish a network connection.NetworkStream
.Streams support several common operations:
Read
: Reads data from the stream.Write
: Writes data to the stream.Seek
: Moves to a specific position in the stream.Close
: Closes the stream to release resources.using System;
using System.IO;
class Program
{
static void Main()
{
string path = "example.txt";
// Write to file
using (FileStream fs = new FileStream(path, FileMode.Create))
{
byte[] content = System.Text.Encoding.UTF8.GetBytes("Hello, C# Streams!");
fs.Write(content, 0, content.Length);
}
// Read from file
using (FileStream fs = new FileStream(path, FileMode.Open))
{
byte[] buffer = new byte[1024];
int bytesRead = fs.Read(buffer, 0, buffer.Length);
Console.WriteLine("Read from file: " + System.Text.Encoding.UTF8.GetString(buffer, 0, bytesRead));
}
}
}
example.txt
using FileStream
.FileMode.Create
creates the file, and FileMode.Open
opens it for reading.In many applications, reading and writing files is crucial. Let’s consider a real-world example of an application that logs messages to a file using FileStream
.
using System;
using System.IO;
public class Logger
{
private readonly string _filePath;
public Logger(string filePath)
{
_filePath = filePath;
}
public void LogMessage(string message)
{
using (FileStream fs = new FileStream(_filePath, FileMode.Append, FileAccess.Write))
{
byte[] content = System.Text.Encoding.UTF8.GetBytes($"{DateTime.Now}: {message}\n");
fs.Write(content, 0, content.Length);
}
}
public void DisplayLog()
{
using (FileStream fs = new FileStream(_filePath, FileMode.Open, FileAccess.Read))
{
using (StreamReader reader = new StreamReader(fs))
{
Console.WriteLine("Log Contents:\n" + reader.ReadToEnd());
}
}
}
}
class Program
{
static void Main()
{
Logger logger = new Logger("log.txt");
logger.LogMessage("Application started");
logger.LogMessage("Application processing data");
logger.LogMessage("Application stopped");
logger.DisplayLog();
}
}
Logger
Class: This class encapsulates methods for logging messages to a file.LogMessage
: Writes a timestamped message to log.txt
, appending it to the existing content.DisplayLog
: Reads and displays the contents of log.txt
.FileStream
, MemoryStream
, NetworkStream
: Each type of stream serves a unique purpose, whether for disk-based storage, in-memory data manipulation, or network communication.Streams in C# are an essential tool for handling data input and output in a controlled, efficient way. From FileStream
for file operations to MemoryStream
for temporary in-memory data storage and NetworkStream
for network communications, streams enable developers to work with various data sources seamlessly. With features like reading, writing, seeking, and closing, streams provide a flexible, consistent approach to data management in any C# application.
By understanding how to implement streams in real-world applications, like our logger system example, you can create efficient, scalable, and reliable data handling components that make your applications more robust and responsive. Mastering streams will elevate your skills in managing data and creating applications that handle data more effectively.