Skip to main content

Migration Guides

This document provides guidance for developers migrating from older C# versions to newer ones, including breaking changes, best practices, and recommended approaches.

Migrating to C# 13

C# 13 introduces several new features and some potential breaking changes. This guide will help you navigate the migration process.

Breaking Changes in C# 13

  1. Interceptors and Source Generators

    • Interceptors may conflict with existing source generators
    • Solution: Update source generators to be aware of interceptors or disable interceptors for specific methods
  2. params Collections

    • Method resolution may change when using params with collection types
    • Solution: Review method overloads that use params and ensure correct resolution
  3. ref struct Interfaces

    • Existing code that assumes ref struct types cannot implement interfaces may break
    • Solution: Review code that makes assumptions about ref struct capabilities

Migration Steps for C# 13

  1. Update Development Environment

    • Install the latest .NET SDK that supports C# 13
    • Update Visual Studio or other IDE to the latest version
  2. Project Configuration

    <PropertyGroup>
    <TargetFramework>net9.0</TargetFramework>
    <LangVersion>13.0</LangVersion>
    </PropertyGroup>
  3. Adopt New Features Incrementally

    • Start with non-breaking features like params collections
    • Test thoroughly before adopting more complex features like interceptors
  4. Update Dependencies

    • Ensure all NuGet packages and libraries are compatible with C# 13
    • Check for updated versions of packages that leverage C# 13 features
  5. Code Review Checklist

    • Review uses of ref struct types for potential interface implementations
    • Check method overloads that might be affected by params collections
    • Review code that might benefit from the new Lock type for thread synchronization

Best Practices for C# 13 Migration

  1. Use params with Span<T> for Performance

    // Before (C# 12 and earlier)
    public void ProcessNumbers(params int[] numbers) { ... }

    // After (C# 13)
    public void ProcessNumbers(params Span<int> numbers) { ... }
  2. Implement Interfaces for ref struct Types

    public interface IBufferReader
    {
    int Read(Span<byte> buffer);
    }

    public ref struct FastBuffer : IBufferReader
    {
    public int Read(Span<byte> buffer) { ... }
    }
  3. Use the New Lock Type for Thread Synchronization

    // Before (C# 12 and earlier)
    private readonly object _lock = new object();

    public void ThreadSafeOperation()
    {
    lock (_lock)
    {
    // Critical section
    }
    }

    // After (C# 13)
    private readonly Lock _lock = new Lock();

    public void ThreadSafeOperation()
    {
    lock (_lock)
    {
    // Critical section
    }
    }

Migrating to C# 14

C# 14 introduces several powerful features and some potential breaking changes. This guide will help you navigate the migration process.

Breaking Changes in C# 14

  1. Extension Members

    • Extension members may conflict with existing instance members
    • Solution: Review extension methods that might conflict with new extension properties or indexers
  2. Null-conditional Assignment

    • Code that relies on the right side of assignments always being evaluated may break
    • Solution: Review code that has side effects in assignments
  3. nameof with Unbound Generic Types

    • Code that parses nameof expressions may need updates
    • Solution: Update code that parses or processes nameof expressions
  4. Span Conversions

    • Method resolution may change due to new implicit conversions
    • Solution: Review method overloads that take Span<T> or ReadOnlySpan<T>

Migration Steps for C# 14

  1. Update Development Environment

    • Install the latest .NET SDK that supports C# 14
    • Update Visual Studio or other IDE to the latest version
  2. Project Configuration

    <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <LangVersion>14.0</LangVersion>
    </PropertyGroup>
  3. Adopt New Features Incrementally

    • Start with non-breaking features like nameof enhancements
    • Test thoroughly before adopting more complex features like extension members
  4. Update Dependencies

    • Ensure all NuGet packages and libraries are compatible with C# 14
    • Check for updated versions of packages that leverage C# 14 features
  5. Code Review Checklist

    • Review extension methods that might be affected by extension members
    • Check code that relies on assignment side effects
    • Review method overloads that might be affected by new span conversions

Best Practices for C# 14 Migration

  1. Use Extension Members for Enhanced APIs

    // Before (C# 13 and earlier)
    public static class StringExtensions
    {
    public static int WordCount(this string str)
    {
    return str.Split(' ').Length;
    }
    }

    // After (C# 14)
    public static class StringExtensions
    {
    extension(string str)
    {
    public int WordCount => str.Split(' ').Length;
    }
    }
  2. Simplify Null Checks with Null-conditional Assignment

    // Before (C# 13 and earlier)
    if (customer != null)
    {
    customer.Name = "John";
    }

    // After (C# 14)
    customer?.Name = "John";
  3. Use nameof with Unbound Generic Types

    // Before (C# 13 and earlier)
    string listName = nameof(List<int>); // "List"

    // After (C# 14)
    string listName = nameof(List<>); // "List"
  4. Leverage Implicit Span Conversions

    // Before (C# 13 and earlier)
    void ProcessText(ReadOnlySpan<char> text) { ... }
    string message = "Hello";
    ProcessText(message.AsSpan()); // Explicit conversion

    // After (C# 14)
    void ProcessText(ReadOnlySpan<char> text) { ... }
    string message = "Hello";
    ProcessText(message); // Implicit conversion

General Migration Best Practices

  1. Use Feature Flags for Gradual Adoption

    • Use conditional compilation to gradually adopt new features
    • This allows for easier rollback if issues are encountered
    #if USE_CS14_FEATURES
    customer?.Name = "John";
    #else
    if (customer != null)
    {
    customer.Name = "John";
    }
    #endif
  2. Comprehensive Testing

    • Ensure thorough test coverage before and after migration
    • Pay special attention to areas affected by breaking changes
  3. Documentation

    • Document which C# version features are being used
    • Provide guidelines for team members on how to use new features
  4. Performance Monitoring

    • Monitor application performance before and after migration
    • New features like Span<T> conversions and params collections can improve performance
  5. Incremental Adoption

    • Migrate one project or component at a time
    • Start with less critical components to minimize risk

By following these guidelines, you can successfully migrate your codebase to newer C# versions while minimizing disruption and maximizing the benefits of new language features.