Category

Challenges

A .NET coding puzzle: Can strings change?

The holiday season is upon us, and what better way to celebrate than with a festive coding challenge?
As part of this year’s “C# Advent” event, I’m sharing a tricky little puzzle that combines holiday cheer with some .NET and C# challenge. If you are a seasoned .NET developer and enjoy solving unique challenges, you might enjoy this one.

Disclaimer
This coding challenge is designed purely for fun and to explore some of .NET’s internal mechanics. It showcases advanced techniques that can be harmful if not handled carefully and you probably should never use this example in real-world production code.

The Challenge

In the spirit of the season, let’s take a look at the problem:

void Main()
{
    Initialize();

    string happyHolidays = "Merry Christmas";
 
    System.Console.WriteLine(happyHolidays); // should output: Advent of C#

    static void Initialize()
    {
        // Your implementation here
    }
}

Your task is to modify the “Initialize” method so that the program outputs exactly: Advent of C#

Here are the constraints:

  • You are only allowed to modify the implementation of the Initialize method.
  • You cannot change the method name, parameters, or signature.
  • You cannot alter any other parts of the program.
  • You cannot use System.Console.WriteLine (or similar) in the Initialize method.
  • You cannot change the default output stream.

This is one of my favorite challenges, it requires some out-of-the-box thinking and a deep knowledge in how Strings works in the .NET runtime.
Before diving into the solution, give yourself a minute to try to solve it!

Solution Breakdown

When solving this challenge, we need to remember that the primary task is to modify the Initialize method in a way that allows the happyHolidays variable to reflect the string “Advent of C#” instead of “Merry Christmas.” Since we’re restricted to modifying only the Initialize method, the solution lies in:

  1. Exploring how strings work in C#: Strings in .NET are immutable, which means they cannot be changed after they are created. However, their references can be manipulated under certain conditions.
  2. Using an internal implementation detail of the runtime and the C# compiler: Since the “Initialize” method is static we can’t directly access the “happyHolidays” variable, therefore we have to be clever and use knowledge of how both the C# compiler and the .NET runtime treat strings.

Solution

There are multiple ways to code the solution, some of which are more elegant that others, but all solutions uses the same tricks:

  1. The string “Merry Christmas” is defined as a constant, and the compiler optimizes for efficiency by eliminating duplicate string constants. To achieve this, it ensures that each constant string is interned.
    In .NET, the runtime uses a String Intern Pool, which acts as a cache to store a single instance of each string. This allows multiple references to point to the same string, saving memory. Since strings are immutable, this sharing doesn’t cause any issues.
    While strings are not automatically added to the intern pool at runtime and requires an explicit call to “String.Intern”, constant strings are interned by default by the compiler.
  2. In .NET, strings are immutable, but this immutability is enforced by the provided APIs. While there are no official public APIs to modify the value of a string instance, this doesn’t mean a string’s value is entirely unchangeable. Using C# features like unsafe code and pointers, it is possible to directly access and modify the string’s memory, bypassing the usual immutability constraints.
  3. Since “Merry Christmas” is longer than “Advent of C#”, we can reuse the existing memory allocated for “Merry Christmas” without needing to allocate new memory. Making a string shorter is easier than making it longer.

Solution #1 – not the most elegant solution

static void Initialize()
{
    string adventOfCsharp= "Advent of C#";
    string happyHolidays = "Merry Christmas";

    unsafe
    {
        fixed (char* happyHolidaysPointer = happyHolidays)
        {
            for (int i = 0; i < adventOfCsharp.Length; i++)
            {
                happyHolidaysPointer[i] = adventOfCsharp[i];
            }
        }
    }

    typeof(string)
        .GetField("_stringLength", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)
        .SetValue(happyHolidays, adventOfCsharp.Length);
}

Solution explanation

This solution works by directly modifying the memory and metadata of the interned string “Merry Christmas”, replacing its content with “Advent of C#”.

  1. String Memory Modification:
    • The unsafe block and the fixed keyword pin the memory of the happyHolidays string, allowing direct pointer access.
    • A loop overwrites the characters in happyHolidays with the corresponding characters from adventOfCsharp
  2. Updating String Metadata:
      • The typeof(string).GetField() retrieves the private _stringLength field, which stores the length of the string.
      • Using reflection, the length of happyHolidays is updated to match the new content, ensuring that subsequent operations recognize the string’s new length.

     

Solution #2 – the elegant solution

static void Initialize()
{ 
    var source = "Merry Christmas";
    var target = "Advent of C#";
    ref var sourceRef = ref MemoryMarshal.GetReference(source.AsSpan());
    var sourceSpan = MemoryMarshal.CreateSpan(ref sourceRef, source.Length);
    target.AsSpan().CopyTo(sourceSpan);
    Unsafe.Subtract(ref Unsafe.As<char, int>(ref sourceRef), 1) = target.Length;
}

Solution explanation

This solution is safer since it doesn’t use “unsafe” and direct pointer access, instead it works by using the MemoryMarshal which is less error-prone than raw pointer manipulation.

  1. Memory Modification:
    • Uses MemoryMarshal.GetReference to retrieve a reference to the string’s memory.
    • Creates a mutable Span from the reference, allowing efficient memory modification without explicit pointer arithmetic.
  2. Length Update:
    • Uses Unsafe.As<char, int> and Unsafe.Subtract to directly modify the string’s length, as the length stored in memory just before the start of the string’s content
    • Avoids reflection entirely, making the solution faster and more resilient to changes in .NET internals.

Important
Although this solution seem safer since it avoids direct pointer manipulation and pointer arithmetic, using these APIs can still performs operations that violate the runtime’s assumptions, making it inherently risky despite being arguably more elegant.
Both solutions are not really safe to use if you are not careful, and depending on the situation, it is easy to make mistakes and introduce hard to track bugs or even interrupt with GC/JIT optimizations.
Use both of these APIs in the scenarios they were intended to be used and by being very careful.

Key Takeaways

This challenge highlights the importance of understanding how strings and memory work under the hood. While direct memory manipulation is rarely needed in day-to-day programming, it’s a valuable tool for understanding how things work behind the scenes.

And most importantly, this challenge showcases advanced .NET techniques for learning purposes only. You probably should not use these methods in production code 🙂