C# Compiler Breaking Changes Since C# 11 - Microsoft Learn
C# Compiler Breaking Changes Since C# 11 - Microsoft Learn
This document lists known breaking changes in Roslyn after .NET 7 general release (.NET
SDK version 7.0.100) through .NET 8 general release (.NET SDK version 8.0.100).
Previously, a mismatch was allowed at compile time, delaying the overload resolution
failure to runtime.
For example, the following code used to compile without an error, but was failing with
exception: "Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: The best
overloaded method match for 'C.f(ref object)' has some invalid arguments" It is going to
produce a compilation error now.
C#
public class C
{
public void f(ref dynamic a)
{
}
requires the elements in the collection expression are implicitly convertible to the
object . Previously, the elements of a collection expression targeting an IEnumerable
elements in the collection expression must be implicitly convertible to the iteration type
of the target type.
C#
Actions a = [_ => { }]; // error CS8917: The delegate type could not be
inferred.
C#
requires the target type to have an accessible constructor that can be called with no
arguments and, if the collection expression is not empty, the target type must have an
accessible Add method that can be called with a single argument.
Previously, the constructor and Add methods were required for construction of the
collection instance but not for conversion. That meant the following call was ambiguous
since both char[] and string were valid target types for the collection expression. The
call is no longer ambiguous because string does not have a parameterless constructor
or Add method.
C#
Feature ref readonly parameters relaxed overload resolution allowing ref arguments
to be passed to in parameters when LangVersion is set to 12 or later. This can lead to
behavior or source breaking changes:
cs
var i = 5;
System.Console.Write(new C().M(ref i)); // prints "E" in C# 11, but "C" in
C# 12
System.Console.Write(E.M(new C(), ref i)); // workaround: prints "E" always
class C
{
public string M(in int i) => "C";
cs
var i = 5;
System.Console.Write(C.M(null, ref i)); // prints "1" in C# 11, but fails
with an ambiguity error in C# 12
System.Console.Write(C.M((I1)null, ref i)); // workaround: prints "1" always
interface I1 { }
interface I2 { }
static class C
{
public static string M(I1 o, ref int x) => "1";
public static string M(I2 o, in int x) => "2";
}
For instance, the public DisposeAsync() method will be picked, rather than the private
interface implementation:
C#