---
title: "Avoiding reflection in C# in way unsafer ways!"
description: "For good reasons, many folks don't want to use **reflection**. Since .NET 8, [we have a better way](https://steven-giesel.com/blogPost/05ecdd16-8dc4-490f-b1cf-780c994346a4/a-new-way-of-doing-reflection-with-net-8) of dealing with this in most of the cases (when known in advance). But we can go the opposite: Make it even unsafer for a tiny bit performance!"
updated: 2025-05-26
tags: ["Reflection", ".NET"]
canonical: "https://steven-giesel.com/blogPost/53691229-fbb1-48cf-95e5-f586f9800ee1/avoiding-reflection-in-c-in-way-unsafer-ways"
---

# Avoiding reflection in C# in way unsafer ways!

For good reasons, many folks don't want to use **reflection**. Since .NET 8, [we have a better way](https://steven-giesel.com/blogPost/05ecdd16-8dc4-490f-b1cf-780c994346a4/a-new-way-of-doing-reflection-with-net-8) of dealing with this in most of the cases (when known in advance). But we can go the opposite: Make it even unsafer for a tiny bit performance!

--------

**Disclaimer**: Needless to say this blog post is just for fun, and is just for educational and "entertaining" purposes.

------

## Objects with private fields

Imagine we have the following object:
```csharp
public class SecretContainer(int initialValue)
{
    private int _otherField = 2;
    private int _secret = initialValue;
}
```

And we want to get the value of `_secret`. With .NET 8, we can do things like:

```csharp
[UnsafeAccessor(UnsafeAccessorKind.Field, Name = "_secret")]
static extern ref int GetCountField(SecretContainer secret);
```

And that gives us the correct answer. But let's make it unsecure as hack! Just because it is possible

## Memory-Layout

We can use our knowledge about the object itself. We know that the second field is indeed the thing we are looking for. So we can get the "raw" (managed) memory of the object and get the slice we need.

```csharp
static unsafe void DisplayPrivateField(SecretContainer secret)
{
    var handle = GCHandle.Alloc(secret, GCHandleType.Pinned);
    try
    {
        // Get pointer to the start of the object
        var ptr = handle.AddrOfPinnedObject();

        // Create Span<byte> over object's memory
        // As we have two int fields, we need to allocate 8 bytes at least
        // And hopefully there is no padding between the fields, ...
        var memory = new Span<byte>((void*)ptr, sizeof(int) * 2);

        // Skip 4 bytes because of the "_otherField"
        const int numberOffset = sizeof(int);
        var number = MemoryMarshal.Read<int>(memory[numberOffset..]);
        Console.WriteLine($"number = {number}");
    }
    finally
    {
        handle.Free();
    }
}
```

This "beauty" gives us the value of `_secret` without using reflection. Of course this has more problems than solutions:
 * The object must be pinned (otherwise the GC can move it)
 * We are relying on the memory layout of the object (which can change in future versions - imagine someone moving the field). But not only that, imagine things like the new `field` keyword in C# 14. We might be at the mercy of the compiler.

But interesting nevertheless.