C# 10 and 11 Features — Global Usings, File-Scoped Namespaces, Required Members

C# 10 and 11 introduced several features that reduce everyday boilerplate in .NET 6+ projects. These are not major language redesigns — they are quality-of-life improvements that make code more concise and signal intent more clearly. In a typical ASP.NET Core project, global usings, file-scoped namespaces, and required members together eliminate dozens of repetitive lines per file. Understanding them is important because modern .NET codebases use them universally, and the syntax will appear unfamiliar if you have only worked with older C# versions.

Global Using Directives (C# 10)

// ── Without global usings — every file repeats these imports ──────────────
// PostsController.cs
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using BlogApp.Application.Interfaces;
using BlogApp.Domain.Entities;

// ── With global usings — declared once, apply to entire project ───────────
// GlobalUsings.cs (or any .cs file)
global using System;
global using System.Collections.Generic;
global using System.Linq;
global using Microsoft.AspNetCore.Mvc;
global using Microsoft.Extensions.Logging;
global using BlogApp.Application.Interfaces;
global using BlogApp.Domain.Entities;

// PostsController.cs — now has zero using statements!
// All global usings are available automatically

// ── ImplicitUsings — .NET 6+ automatically adds common BCL namespaces ─────
// In .csproj: <ImplicitUsings>enable</ImplicitUsings>
// Automatically adds: System, System.Linq, System.Collections.Generic,
//                     System.IO, System.Threading.Tasks, etc.
// For web projects: also Microsoft.AspNetCore.Builder, Http, Routing, etc.
Note: Global usings are applied at the project level — they appear in every C# file in the project. Implicit usings (the .csproj setting) are a layer on top: .NET 6+ adds a set of commonly-needed BCL namespaces automatically. You still need global usings for your own application namespaces and third-party libraries. A common convention is to create a single GlobalUsings.cs file at the project root with all custom global usings, making it easy to see and manage what is globally imported.
Tip: Do not add every namespace you ever use to global usings — only add the ones that are used in the majority of files (5+ files). Adding rarely-used namespaces globally can cause naming conflicts (where two namespaces define a type with the same name) and makes the code harder to understand because readers cannot see where types come from. Reserve global usings for framework namespaces and your own core application namespaces.
Warning: File-scoped namespaces (the new single-line namespace BlogApp.Api; syntax) and block-scoped namespaces (the traditional curly-brace style) cannot be mixed in the same file. Pick one style consistently across the project. Most modern .NET 6+ projects use file-scoped namespaces as they save one level of indentation across every file. Configure your IDE and .editorconfig to enforce the chosen style.

File-Scoped Namespaces (C# 10)

// ── Traditional block-scoped namespace — one extra level of indentation ───
namespace BlogApp.Controllers
{
    public class PostsController : ControllerBase
    {
        // All code indented one level under the namespace block
    }
}

// ── File-scoped namespace — no indentation overhead ────────────────────────
namespace BlogApp.Controllers;   // ← semicolon, applies to entire file

public class PostsController : ControllerBase
{
    // Code at top level — no namespace indentation
    private readonly IPostService _service;

    public PostsController(IPostService service) => _service = service;

    [HttpGet("{id:int}")]
    public async Task<IActionResult> GetById(int id, CancellationToken ct)
    {
        var post = await _service.GetByIdAsync(id, ct);
        return Ok(post);
    }
}

Required Members (C# 11)

// The required modifier — compiler enforces the property must be set during initialisation
public class Post
{
    public required int    Id     { get; init; }    // must be set in object initialiser
    public required string Title  { get; init; }    // must be set
    public          string Body   { get; init; } = string.Empty;   // optional (has default)
    public required string AuthorId { get; init; }  // must be set
}

// ✅ Valid — all required properties set
var post = new Post
{
    Id       = 1,
    Title    = "Hello World",
    AuthorId = "user-42",
    // Body is optional — defaults to string.Empty
};

// ❌ Compile error — required property Id not initialised
var bad = new Post { Title = "Hello" };   // compile error: Id is required

Raw String Literals (C# 11)

// At least 3 quotes — no escaping needed inside
string json = """
    {
        "name": "Alice",
        "roles": ["admin", "editor"],
        "metadata": { "created": "2025-01-15" }
    }
    """;

// Combine with interpolation
string name = "Alice";
string jsonInterp = $"""
    {{
        "name": "{name}",
        "timestamp": "{DateTime.UtcNow:O}"
    }}
    """;  // {{ and }} = literal braces; {expr} = interpolation

Common Mistakes

Mistake 1 — Mixing file-scoped and block-scoped namespaces in one file

❌ Wrong — compile error: cannot mix both styles in one file.

✅ Correct — choose one style project-wide and enforce via .editorconfig.

Mistake 2 — Adding too many namespaces to global usings (naming conflicts)

❌ Wrong — global using Newtonsoft.Json; and global using System.Text.Json; both define JsonSerializer — compiler cannot resolve which one you mean.

✅ Correct — add only the namespace whose type you want to be the default; explicitly qualify the other.

🧠 Test Yourself

A class has two required properties: Title and AuthorId. Someone creates an instance with only Title set. When does the error occur?