C#
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C#
-
Section 4: Working with Data
-
Section 5: Error Handling
-
Section 6: Delegates and Events
-
Section 7: Async Programming
-
Section 8: More Language Features
-
Section 9: File I/O and Serialization
-
Section 10: Networking in .NET
-
Section 11: The .NET Ecosystem
-
Section 12: Memory and Performance
-
Section 13: Concurrency Beyond Async
-
Section 14: Reflection and Attributes
-
Section 15: Testing and Best Practices
-
Section 16: Design Patterns in C#
-
Section 17: Standard Library Deep Dive
-
Section 18: Data Structures and Algorithms in C#
-
Section 19: GUI and Desktop Development Overview
-
Section 20: Practical Projects
-
Section 21: More Practice Exercises
-
Section 22: More Standard Library and Text Processing
-
Section 23: More Design Patterns
-
Section 24: More Projects
-
Section 25: Interview Practice
-
Section 26: C# Keywords Reference (Modifiers)
-
Section 27: C# Keywords Reference (Statements)
-
Section 28: C# Keywords Reference (Operators)
-
Section 29: BCL: System.Collections.Generic
-
Section 30: BCL: System.Linq
-
Section 31: BCL: System.Threading
-
Section 32: BCL: System.IO
-
Section 33: BCL: System.Text and System.Text.Json
-
Section 34: BCL: System.Net.Http
-
Section 35: C# Language Specification Topics
-
Section 36: More Practice Exercises
-
Section 37: More Async Patterns
-
Section 38: More BCL: System.Reflection and System.Diagnostics
-
Section 39: Nullable Reference Types In Depth
-
Section 40: C# Records and Pattern Matching In Depth
-
Section 41: Dependency Injection Deep Dive
-
Section 42: More Interview and Whiteboard Practice
203: MemoryStream
I've seen this exact bug pop up in almost every junior dev's first attempt at handling binary data. You're working with a MemoryStream, you write some data to it, and then you immediately try to read that data back—only to find that the reader returns nothing, or worse, throws an EndOfStreamException.
public byte[] GetPacketData()
{
using (var ms = new MemoryStream())
{
using (var writer = new BinaryWriter(ms))
{
writer.Write(101); // Packet ID
writer.Write("Hello"); // Payload
}
// We want to return the bytes we just wrote
using (var reader = new BinaryReader(ms))
{
// This will throw EndOfStreamException!
int id = reader.ReadInt32();
return ms.ToArray();
}
}
}
The Empty Read Mystery
If you look at the code above, it seems logically sound. You opened a stream, wrote an integer and a string, and then tried to read them. So why does it crash?
Here is the thing about streams: they aren't just buckets of data; they have a position. Think of it like a cassette tape or a record player needle. When you wrote the Packet ID and the payload, the "needle" moved forward with every byte written. By the time the BinaryWriter finished, the position was at the very end of the stream.
When the BinaryReader starts, it doesn't automatically jump back to the beginning. It starts exactly where the writer left off—at the end. It looks forward, sees there's nothing left to read, and panics.
Resetting the Stream Pointer
The fix is simple, but it's the one thing everyone forgets. You have to manually move the position back to zero before you start reading. I usually do this explicitly right after the writer is disposed.
public byte[] GetPacketData()
{
using (var ms = new MemoryStream())
{
using (var writer = new BinaryWriter(ms))
{
writer.Write(101);
writer.Write("Hello");
}
// This is the magic line. Reset the needle to the start.
ms.Position = 0;
using (var reader = new BinaryReader(ms))
{
int id = reader.ReadInt32(); // Now this works!
return ms.ToArray();
}
}
}
Now, you might be wondering if you can just use ms.ToArray() and skip the reader entirely. You can, but MemoryStream is incredibly powerful when you need to build a complex binary object in memory before sending it over a network or saving it to a file. It avoids the overhead of creating multiple temporary byte arrays and concatenating them, which is a nightmare for the Garbage Collector.
One last pro tip: be careful with ms.GetBuffer(). It returns the internal array used by the stream, which is often larger than the actual data you wrote. If you need exactly the bytes you wrote and nothing more, stick with ToArray(), even though it creates a copy. It'll save you from a lot of "where did these extra zeros come from?" debugging sessions.
📋 Practical Task
Exercise: Building a Binary Message Packetizer
Your task is to create a class called MessagePacketizer that mimics a simple network protocol. The protocol requires that every message starts with a 4-byte integer representing the length of the message, followed by the message string itself in UTF-8 encoding.
Implement a method byte[] CreatePacket(string message) that:
- Uses a
MemoryStreamandBinaryWriterto write the length of the string (in bytes) as anInt32. - Writes the actual string bytes to the stream.
- Resets the stream position to 0.
- Uses a
BinaryReaderto read the length back first, then the string, just to verify the data is correct before returning the finalToArray()result.
Test your code by passing the string "C# MemoryStreams are powerful!" and ensuring the resulting byte array starts with the correct length integer.
There are no comments for now.