If you’ve read the official announcement about generics in .NET nanoFramework, you already have the “what” and the “how”. This post is the other half: the “why”, the “when”, and the very human parts in between.
Because, honestly, this journey didn’t start with a grand roadmap, an architecture meeting, or a neatly planned milestone.
It started during the COVID lockdowns.
Early 2020: confinement, curiosity, and a slightly dangerous mix
Back in early 2020, the world went into lockdown and, like many of you, I suddenly had more time in my head than in my calendar. And when that happens to developers, things tend to… happen.
Generics have been a fundamental feature in desktop .NET for years. But when .NET Micro Framework (.NETMF) first appeared, generics weren’t even a thing yet. That has a bigger impact than it sounds: the original building blocks weren’t designed with generics in mind, because the feature simply didn’t exist.
When generics eventually arrived in C#, the question naturally came up in the .NETMF world too. The answer was almost always the same, and it usually sounded like some variation of:
- too much memory required
- too much performance impact
- code size will bloat
- and a major implementation challenge
nanoFramework was no different. As the project matured and gained momentum, developers started asking the same questions – and the replies were usually based on the same assumptions. And, to be fair, if the people who invented .NETMF kept saying it wasn’t doable, that must be correct… right?
Still, generics remained one of those “it would be amazing, but…” topics. You know the kind:
- “Wouldn’t it be great if we had generics?”
- “Yeah, but… this can’t be done on these platforms.”
And here’s the thing about that sentence – “this can’t be done” – it tends to have a strange effect on my engineer brain.
It’s almost like a switch flips and I immediately want to test whether “can’t” means “physically impossible” or just “nobody has tried hard enough yet”.
So yes: a mix of stubbornness and curiosity. Possibly not the most responsible cocktail, but definitely a productive one.
The “Boldly Go” Moment
I’ve joked about it more than once, but it’s true: when I hear “this can’t be done”, it triggers that very specific urge to boldly go where no developer has gone before.
Not because I enjoy suffering (well… not always), but because difficult problems tend to hide the most interesting lessons. And if there’s one thing I love, it’s learning something new that also happens to make the platform better for everyone.
I also realized pretty quickly that this wouldn’t be a “single-module” change. Generics touch everything: the execution engine, the type system, the toolchain, the metadata processor, and yes, the debugger too.
So it began – with a naive C# application and one small generic class.
Roslyn compiled it and the metadata processor parsed it… and then loading it on nanoFramework failed (exactly as expected). From there, it became “just” a matter of peeling the onion. Backward.
It turned into a ping-pong between the metadata processor and the runtime type system: understand what was missing, decide what was absolutely required (without inflating nanoFramework beyond what makes sense), implement, test, repeat. Because this spans multiple components – some in C#, others in C and C++ – every iteration took time and demanded a lot of patience and care. Every change had to be validated, and every fix had to prove it wasn’t silently breaking something else.
Eventually, the iterations got longer… and things got more stable.
At some point, the debugger had to be brought in. After all, code using generics should be debugged like any other code, right?
For a few reasons, I kept most of this work quiet at the time: it was still fragile, I didn’t want to overpromise, and I also didn’t want to distract the team (or the community) with something that might still hit a hard wall. Only the core team knew the details. When I finally felt confident that this was a minimal viable concept, I was excited enough to tweet about it and share a screenshot of the trace from that naive app.
A hard task – and a very quiet internet
Once I got serious about it, reality hit quickly.
This hadn’t really been done before in these constrained environments in a way that stayed faithful to the .NET model and still fit inside the nanoFramework world.
And the usual “developer safety net” wasn’t there:
- no helpful blog posts
- no random GitHub repos to peek at
- no “someone on Stack Overflow already solved this in 2014”
- no expert I could casually ping with “quick question about MethodSpecs”.
It was just me… and the spec.
At one point, I remember feeling so comically alone in it that I tweeted what felt like a perfect representation of developing generics for nanoFramework: a picture of Lucky Luke riding into the sunset.
Because that’s honestly what it felt like: heading into a big quiet desert with a problem, a plan, and no idea where the next water stop would be.
ECMA-335: on of mine unlikely lockdown companions
If you’ve never had ECMA-335 as bedtime reading… let’s just say it’s not exactly a page-turner.
But it’s also a masterpiece.
That specification became my guide, my reference, my “okay, so what does the CLI actually promise here?”, and occasionally my “wait… that’s how they intended it?”
I had to learn so much:
- metadata details I didn’t even know existed
- how generics are represented and resolved
- how signatures carry the story
- and how the runtime is supposed to reason about it all.
There were plenty of moments where I had to stop, reread, draw diagrams, and reconstruct the mental model piece by piece.
The surprise: how beautifully “simple” it is
And this is the part that still makes me smile.
After all the fear, all the complexity, all the “this must be impossible on a microcontroller”… I was genuinely surprised by the elegance of the underlying reasoning.
Generics are not magic. They’re not “special runtime fairy dust”.
They are, in a very real sense, a carefully defined set of rules encoded into metadata.
Once you see how it’s captured in things like:
- signatures
- TypeSpecs
- MethodSpecs
- GenericParams
…you start to realize it’s all there. Beautifully folded into the metadata in a way that a runtime can systematically unfold.
And at the end of the day, a generic type doesn’t remain some abstract concept floating above the execution engine. It gets resolved, specialized, and becomes something concrete. Something that the interpreter can run.
A “generic” ends up – effectively – as a regular type… just reached through a more powerful path.
That moment, when the mental model clicks and the complexity collapses into something understandable, is one of my favorite feelings in the engineering thought process.
Then life came back (and the project slowed down)
Of course, confinement didn’t last forever.
Once daily work ramped up again, the available “deep focus time” started shrinking. And as anyone who’s worked on hard runtime features knows, generics aren’t the kind of thing you casually chip away at in 20-minute intervals between meetings.
So progress slowed. Then stalled.
Not because the idea stopped being important, and not because the goal became less exciting – but because time is a very real constraint, and real life has a habit of demanding attention.
And still… the work was there. The understanding was there. The foundation was there. Waiting patiently.
2024: a serious boost (thank you, Skyworks)
In 2024, during a work project with Skyworks Inc., they showed interest in having generics in .NET nanoFramework. Beyond the feature itself, it would make their lives significantly easier when reusing and sharing code between desktop .NET and nanoFramework.
Skyworks decided to sponsor the work – and that’s when generics became active again in a serious way.
It’s more than fair that I acknowledge the people who backed this. In particular: Mike Muegel (Technical Director and Software Engineer), who has been a great friend and supporter of the project; Zehra Gozde Fidan (then Director of Solutions Development), who backed the idea of bringing .NET nanoFramework into Skyworks Timing Products’ developer toolbox; and Francisco Tamayo, who took over that role later.
It’s also worth highlighting something I genuinely respect: Skyworks doesn’t just take from open source. They contribute back and help ensure that the projects they depend on are maintained and can thrive.
This sponsorship gave generics development a real boost and moved it forward substantially. Then priorities shifted again, and progress slowed down once more.
2025: back at it, with renewed enthusiasm
Fast forward to 2025. A couple of months before the MVP Summit, it crossed my mind that this would be a great occasion to finally announce that generics were available in .NET nanoFramework.
So I got back to work with renewed enthusiasm.
Barely on time, it happened. Generics were in good enough shape to be used, and I had the pleasure of announcing it to my fellow IoT MVPs. That’s when the private preview started.
Private preview and the “last mile”
Once feedback started arriving, the real “last mile” began.
Work kicked off on the libraries needed to support broader, everyday use of generics – especially collections. And that, in turn, opened a classic can of worms: edge cases and execution paths that simply hadn’t been exercised before. More changes were required in type system structures, decoders, and handlers for IL instructions that interact with generics.
If you want a mental picture, try this:
One moment, 2.000+ unit tests in mscorlib and the metadata processor are all green and life is good.
Then you start adding tests for reported issues, and adding new generic-heavy types like Span<T>, Stack<T>, and List<T>.
And suddenly, a good portion of the test suite turns red.
That moment comes with a special mix of puzzlement and surprise – and, if I’m being honest, a few cursing words (which I’ll skip here to keep this post polite). It’s usually followed by a short burst of panic: “What if this can’t be done after all?”
And then you take a breath and think straight: “Wait. We already had generics running. We’ve tested plenty of scenarios. This has to be something specific – not proof that the whole idea is broken.”
So it was time to do the unglamorous work: go instruction by instruction, check the code behind each IL path, and figure out what was missing or short – then fix it.
The last months went by with slow progress, watching the test suite go green one case at a time. Sometimes several tests would flip to green at once. Hooray.
The slowness wasn’t accidental: each remaining bug (or missing chunk of implementation) was harder to find – and harder to fix – than the previous one.
More often than I’d like to admit, I had to fix the previous fix… and then fix that fix… until it covered all the variations and execution paths.
Along the way, I also discovered that a few IL instructions didn’t even have an implementation. They simply weren’t used by code that doesn’t deal with generics. No worries – let’s tackled those too.
And last but not least, when testing code ported from full .NET, I “stumbled” onto stackalloc. Which is almost a must-have when working with Span<byte> – and Span<byte> is, in turn, close to a must-have for embedded scenarios.
That unlocked another serious batch of work: allocating storage, managing it safely, and making it fit into runtime structures that were never designed to deal with external storage.
Let me write it again so there are no doubts: this was a hard process. Often painful. Sometimes it involved a serious roadblock that lasted long enough to make me wonder whether I’d finally hit an impossible limit.
But persistence, enthusiasm (and yes, a solid portion of stubbornness) brought it to where it is today.
With the public preview, this is not the end of the journey. I’m pretty sure there are still bugs lurking in the code, and the community will find edge cases and execution paths that aren’t covered by the tests yet.
But that’s how it goes in this industry.
Why I’m Sharing This?
I wanted this companion post to exist because big technical features don’t appear out of thin air. They come from late nights, stubborn curiosity, lots of reading, countless wrong turns, and those rare “oh wow, it’s actually elegant” moments.
Generics in .NET nanoFramework are not just a checkbox feature.
They’re a story:
- of a weird period in the world,
- of a personal challenge triggered by “can’t be done”,
- of learning an intimidating spec deeply enough to trust it,
- and of pushing a constrained platform closer to the .NET experience we all love.
And yes—also a story with Lucky Luke riding into the sunset.
I can’t tell you how happy (and proud) I am that all this can be released to public preview. This brings nanoFramework even closer feature-wise to full .NET – and it’s a testament to nanoFramework as a serious development framework.
Have fun with .NET nanoFramework!