Breaking changes in Roslyn after .NET 7.0.100 through .NET 8.0.100
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).
Ref modifiers of dynamic arguments should be compatible with ref modifiers of corresponding parameters
Introduced in Visual Studio 2022 version 17.10
Ref modifiers of dynamic arguments should be compatible with ref modifiers of corresponding parameters at compile time. This can cause an overload resolution involving dynamic arguments to fail at compile time instead of runtime.
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.
public class C
{
public void f(ref dynamic a)
{
}
public void M(dynamic d)
{
f(d); // error CS1620: Argument 1 must be passed with the 'ref' keyword
}
}
Collection expression for type implementing IEnumerable
must have elements implicitly convertible to object
Introduced in Visual Studio 2022 version 17.10
Conversion of a collection expression to a struct
or class
that implements System.Collections.IEnumerable
and does not have a strongly-typed GetEnumerator()
requires the elements in the collection expression are implicitly convertible to the object
.
Previously, the elements of a collection expression targeting an IEnumerable
implementation were assumed to be convertible to object
, and converted only when binding to the applicable Add
method.
This additional requirement means that collection expression conversions to IEnumerable
implementations are treated consistently with other target types where the elements in the collection expression must be implicitly convertible to the iteration type of the target type.
This change affects collection expressions targeting IEnumerable
implementations where the elements rely on target-typing to a strongly-typed Add
method parameter type.
In the example below, an error is reported that _ => { }
cannot be implicitly converted to object
.
class Actions : IEnumerable
{
public void Add(Action<int> action);
// ...
}
Actions a = [_ => { }]; // error CS8917: The delegate type could not be inferred.
To resolve the error, the element expression could be explicitly typed.
a = [(int _) => { }]; // ok
a = [(Action<int>)(_ => { })]; // ok
Collection expression target type must have constructor and Add
method
Introduced in Visual Studio 2022 version 17.10
Conversion of a collection expression to a struct
or class
that implements System.Collections.IEnumerable
and does not have a CollectionBuilderAttribute
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.
Print(['a', 'b', 'c']); // calls Print(char[])
static void Print(char[] arg) { }
static void Print(string arg) { }
ref
arguments can be passed to in
parameters
Introduced in Visual Studio 2022 version 17.8p2
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:
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";
}
static class E
{
public static string M(this C c, ref int i) => "E";
}
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";
}
Prefer pattern-based over interface-based disposal in async using
Introduced in Visual Studio 2022 version 17.10p3
An async using
prefers to bind using a pattern-based DisposeAsync()
method rather than the interface-based IAsyncDisposable.DisposeAsync()
.
For instance, the public DisposeAsync()
method will be picked, rather than the private interface implementation:
await using (var x = new C()) { }
public class C : System.IAsyncDisposable
{
ValueTask IAsyncDisposable.DisposeAsync() => throw null; // no longer picked
public async ValueTask DisposeAsync()
{
Console.WriteLine("PICKED");
await Task.Yield();
}
}
Roslyn breaking changes