Microsoft announced on May 21 that C# is getting a new memory safety model in C# 16—and the trigger is explicit: AI-assisted code generation scales faster than human review. The redesign transforms the unsafe keyword from a syntactic context marker into a caller-facing contract that the compiler actively enforces. Microsoft didn’t bury the rationale: “AI-assisted code generation adds a new dimension as software production scales faster than human review,” reads the .NET Blog post. The unsafe keyword is no longer just about pointers. It’s about telling the compiler—and every automated tool reading your code—where the guardrails end.
What Actually Changes in C# 16
The core shift: calling an unsafe method now requires callers to explicitly acknowledge the obligation. Under the current model, a non-unsafe caller can invoke an unsafe method without ceremony. Under C# 16, every call site requires an inner unsafe { } block. The method signature alone no longer propagates unsafety silently down the call graph.
The before-and-after is stark:
// Current (C# 1-15): Callers invoke unsafe methods without acknowledgment
unsafe void WriteBytes(byte* ptr, int len) { /* dereference */ }
void Caller() { WriteBytes(ptr, len); } // No ceremony required
// C# 16: Every call site must acknowledge the contract
/// <safety>
/// ptr must point to valid allocated memory of at least len bytes.
/// </safety>
unsafe void WriteBytes(byte* ptr, int len) { unsafe { /* dereference */ } }
void Caller() {
unsafe { WriteBytes(ptr, len); } // Obligation acknowledged
}
Every unsafe member now requires a /// <safety> XML documentation block describing caller obligations—Roslyn analyzers will flag missing blocks. Additionally, the unsafe type modifier on classes and structs is being removed entirely; unsafety moves to individual methods, properties, and fields. Pointer types in signatures no longer propagate unsafety on their own; only pointer dereferences do. The accepted dotnet/designs proposal has full technical details.
Why AI Changed the Calculus
Convention-based safety doesn’t audit well. When a developer writes an unsafe method today, the safety contract lives in their head—or, if they’re diligent, in a comment that no tool can validate. That model worked when humans read every line. It doesn’t work when AI agents generate code at scale and developers review diffs rather than lines.
Microsoft’s design proposal puts it directly: “Safe code is well-suited to generative AI. It is easier to understand, review, and modify with confidence.” Internal research shows AI agents double their success rates when given compiler feedback. Making unsafe contracts machine-readable is a prerequisite for AI-assisted development that doesn’t accumulate hidden safety debt. For context: the Cloud Security Alliance tracked 35 CVEs in March 2026 directly attributable to AI-generated code—up from 6 in January. This isn’t hypothetical risk.
Related: Rust 1.96 Ships May 28: Fix Your WebAssembly Builds Now
The New safe Keyword and Documentation Convention
C# 16 also introduces a safe keyword for extern declarations via LibraryImport. Every native call must now make an explicit choice—safe or unsafe. Previously, the distinction was implied by convention and context; now it’s a compile-time declaration.
// Explicitly safe native call—no unsafe context required at call site
[LibraryImport("libc")]
internal static safe partial int getpid();
// Explicitly unsafe FFI with documented obligation
/// <safety>
/// handle must be a valid, non-disposed resource handle.
/// </safety>
[LibraryImport("mylib")]
internal static unsafe partial void dangerousOp(IntPtr handle);
The documentation convention mirrors Rust’s /// # Safety pattern. Implementation sites get // SAFETY: inline comments explaining why a specific operation is permissible. As a result, safety contracts become visible in code review, grep-able by static analysis, and auditable in supply chain tooling. NuGet.org is reportedly considering a safety compliance badge for libraries that adopt the new model—similar to how Rust’s unsafe-free crates get labeled on crates.io.
Timeline and What C# Developers Should Do
The new model is opt-in via a project-level property landing with the .NET 11 preview; production release comes in .NET 12. Legacy C# 1.0 rules remain the default until you explicitly enable the new model. Migration path: run dotnet format to mechanically insert inner unsafe { } blocks at call sites, then write /// <safety> documentation manually. The tool handles syntax; safety semantics require a developer’s judgment.
The government compliance angle is real but not alarming. CISA and the NSA set a January 2026 deadline for vendors to publish memory safety roadmaps, and C# is on their approved languages list alongside Rust, Go, Java, Swift, and Python. However, “approved” always came with an asterisk on the unsafe surface. C# 16 closes that gap. Most C# developers will never touch this directly—unsafe APIs are a small fraction of typical application code—but library authors and teams working on security-sensitive infrastructure should start planning now.
Key Takeaways
- C# 16 redesigns
unsafeas a caller-facing contract: callers must now explicitly acknowledge unsafe obligations viaunsafe { }blocks at every call site - Microsoft designed this explicitly for AI-assisted development—safety contracts must be machine-readable when AI agents are writing and reviewing code, not buried in convention
- A new
safekeyword for extern/FFI declarations eliminates silent unsafety in native interop—everyLibraryImportmust now make an explicit choice - Rollout is opt-in: .NET 11 preview, .NET 12 production;
dotnet formathandles mechanical migration, but/// <safety>docs require human judgment - Most C# developers are unaffected—but library authors and CISA/NSA-compliance teams should start planning the migration now













