TechInterviewNotes

‹ Courses

Guided course

test course

Save

testttdescr

Follow the modules in order, complete each lesson, and use the linked practice tests to check your understanding.

Your progress

Start this course to keep your place across devices.

Sign in to track progress

Module 1

2 lessons
  1. 1
  2. 2

Lesson 1 of 2 | testmodule1

test 1

Everyone is taught "private is private, if you really need it use reflection." Reflection is slow, allocates, boxes, and breaks AOT. .NET 8 gave us a cheat code: [UnsafeAccessor]

What it does

It lets you declare an extern static method that the JIT replaces at compile-time with direct memory access to a private field, method, or constructor. No reflection, no BindingFlags, near-native speed.

The code that flexes in interviews

C#

// Some legacy class you can't changeclass BankAccount {    private decimal _balance = 1000;    private void Audit(string msg) => Console.WriteLine($"Audited: {msg}");}
// Your accessor - this is ALL you writestatic class BankAccountAccessor{    [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "_balance")]    public extern static ref decimal GetBalance(BankAccount acc);
    [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "Audit")]    public extern static void CallAudit(BankAccount acc, string msg);}
// Usagevar acc = new BankAccount();
// 1. Read AND mutate a private field by ref - no setter neededref decimal bal = ref BankAccountAccessor.GetBalance(acc);bal += 500; // you just changed private _balance
// 2. Call a private method like it's publicBankAccountAccessor.CallAudit(acc, "balance fixed");

20 lines hidden

Notice the ref decimal return. You can mutate the private field without reflection. Try doing that cleanly with FieldInfo.

Why this differentiates you

  1. You answer the classic trap better: "How to test private methods?" Most say reflection. You say UnsafeAccessor, zero overhead, AOT and NativeAOT compatible.
  2. You understand the runtime: This is not a hack. It's a JIT intrinsic. The runtime itself uses it internally to avoid InternalsVisibleTo spam.
  3. Real uses: Patching legacy libraries, high-performance access to List<T>._items without ToArray() allocations, unit testing sealed legacy code.

Add to your csproj for .NET 8:

XML

<PropertyGroup>  <AllowUnsafeAccessor>true</AllowUnsafeAccessor></PropertyGroup>

Rule: don't abuse it to break encapsulation everywhere. Use it where performance or testing forces you to cross the boundary.

1. Make your library NativeAOT / AOT ready

Reflection with BindingFlags.NonPublic gets trimmed away when you publish with PublishAot=true. Your app crashes at runtime. UnsafeAccessor is a JIT intrinsic, so the trimmer sees it as a direct access and keeps it.

This is why Microsoft added it. The .NET runtime team themselves use it to access private members across assemblies without adding InternalsVisibleTo everywhere.

2. High-performance code without allocations

Classic example: You have a List<T> with 1M items. If you call ToArray() or even iterate with foreach in a super hot loop, you pay.

With UnsafeAccessor you can grab its internal array directly:

C#

static class ListAccessor<T>{    [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "_items")]    public extern static ref T[] GetItems(List<T> list);        [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "_size")]    public extern static ref int GetSize(List<T> list);}
var list = new List<int> { 1, 2, 3 };ref var items = ref ListAccessor<int>.GetItems(list);ref var size = ref ListAccessor<int>.GetSize(list);
// Now loop over items[0..size] with zero allocation, ~1-2 ns vs 200 ns for reflection

9 lines hidden

This is how high-perf serializers, game engines, and ORMs work.

3. Testing legacy code you can't change

You join a project with a 500-line private method CalculateRisk(). You can't refactor yet, but you need to test it.

Most devs do:
typeof(Service).GetMethod("CalculateRisk", BindingFlags.NonPublic | BindingFlags.Instance)

You do:

C#

[UnsafeAccessor(UnsafeAccessorKind.Method, Name = "CalculateRisk")]extern static decimal TestCalculateRisk(RiskService s, int input);

Type-safe, compile-time checked, 100x faster in tests.

4. Fixing or extending a 3rd party NuGet without forking it

Say a library has a private ConnectionTimeout field with no setter and a bug. You can't wait for the author.

ref int timeout = ref MyLibAccessor.GetTimeout(client); timeout = 10000;

You patched it in memory.

5. Creating objects with private constructors

For deserializers / deep cloners:

C#

[UnsafeAccessor(UnsafeAccessorKind.Constructor)]extern static MyDto CreateDto();

No Activator.CreateInstance overhead.

When NOT to use it: If you own the code, just make it internal and use InternalsVisibleTo. If you are doing normal business logic, don't reach for private state, you will create brittle code.

Think of it like this: Reflection is like asking a manager to go find a file. UnsafeAccessor is having the master key and walking straight to the cabinet.

test course — TechInterviewNotes