Dela via


Brytande ändringar i Roslyn efter .NET 8.0.100 genom .NET 9.0.100

Det här dokumentet listar kända icke-bakåtkompatibla ändringar i Roslyn efter den allmänna versionen av .NET 8 (.NET SDK version 8.0.100) genom den allmänna versionen av .NET 9 (.NET SDK version 9.0.100).

InlineArray-attributet för en rekordstrukturtyp tillåts inte längre.

Introducerades i Visual Studio 2022 version 17.11

[System.Runtime.CompilerServices.InlineArray(10)] // error CS9259: Attribute 'System.Runtime.CompilerServices.InlineArray' cannot be applied to a record struct.
record struct Buffer1()
{
    private int _element0;
}

[System.Runtime.CompilerServices.InlineArray(10)] // error CS9259: Attribute 'System.Runtime.CompilerServices.InlineArray' cannot be applied to a record struct.
record struct Buffer2(int p1)
{
}

Iteratorer introducerar säker kontext i C# 13 och senare

Introducerades i Visual Studio 2022 version 17.11

Även om språkspecifikationen anger att iteratorer introducerar en säker kontext implementerar Roslyn inte det i C# 12 och lägre. Detta ändras i C# 13 som en del av en funktion som tillåter osäker kod i iteratorer. Ändringen bryter inte normala scenarier eftersom det inte var tillåtet att använda osäkra konstruktioner direkt i iteratorer ändå. Det kan dock bryta scenarier där en osäker kontext tidigare ärvdes till kapslade lokala funktioner, till exempel:

unsafe class C // unsafe context
{
    System.Collections.Generic.IEnumerable<int> M() // an iterator
    {
        yield return 1;
        local();
        void local()
        {
            int* p = null; // allowed in C# 12; error in C# 13
        }
    }
}

Du kan kringgå pausen genom att lägga till unsafe modifieraren i den lokala funktionen.

Samlingsuttryck som bryter ändringar med överbelastningsmatchning i C# 13 och senare

Introducerades i Visual Studio 2022 version 17.12 och senare när du använder C# 13+

Det finns några ändringar i bindning av samlingsuttryck i C# 13. De flesta av dessa omvandlar tvetydigheter till lyckade kompileringar, men några är brytningsändringar som antingen resulterar i ett nytt kompileringsfel eller påverkar programmets beteende. De beskrivs nedan.

Tomma samlingsuttryck använder inte längre om ett API är ett intervall för tiebreak vid överlagringar

När ett tomt samlingsuttryck tillhandahålls till en överbelastad metod, och det inte finns någon tydlig elementtyp, använder vi inte längre om ett API tar ett ReadOnlySpan<T> eller ett Span<T> för att avgöra om api:et ska föredras. Till exempel:

class C
{
    static void M(ReadOnlySpan<int> ros) {}
    static void M(Span<object> s) {}

    static void Main()
    {
        M([]); // C.M(ReadOnlySpan<int>) in C# 12, error in C# 13.
    }
}

Exakt elementtyp föredras framför allt annat

I C# 13 föredrar vi en exakt matchning av elementtyp och tittar på konverteringar från uttryck. Detta kan leda till en beteendeförändring när du involverar konstanter:

class C
{
    static void M1(ReadOnlySpan<byte> ros) {}
    static void M1(Span<int> s) {}

    static void M2(ReadOnlySpan<string> ros) {}
    static void M2(Span<CustomInterpolatedStringHandler> ros) {}

    static void Main()
    {
        M1([1]); // C.M(ReadOnlySpan<byte>) in C# 12, C.M(Span<int>) in C# 13

        M2([$"{1}"]); // C.M(ReadOnlySpan<string>) in C# 12, C.M(Span<CustomInterpolatedStringHandler>) in C# 13
    }
}

Deklaration av indexerare i avsaknad av en korrekt deklaration av DefaultMemberAttribute är inte längre tillåten.

introducerades i Visual Studio 2022 version 17.13

public interface I1
{
    public I1 this[I1 args] { get; } // error CS0656: Missing compiler required member 'System.Reflection.DefaultMemberAttribute..ctor'
}

Standard- och paramsparametrar beaktas i metodgruppens naturliga typ

introducerades i Visual Studio 2022 version 17.13

Tidigare har kompilatorn oväntat härledt olika ombudstyper beroende på ordningen på kandidater i källan när standardparametervärden eller params matriser användes. Nu genereras ett tvetydighetsfel.

using System;

class Program
{
    static void Main()
    {
        var x1 = new Program().Test1; // previously Action<long[]> - now error
        var x2 = new Program().Test2; // previously anonymous void delegate(params long[]) - now error

        x1();
        x2();
    }
}

static class E
{
    static public void Test1(this Program p, long[] a) => Console.Write(a.Length);
    static public void Test1(this object p, params long[] a) => Console.Write(a.Length);

    static public void Test2(this object p, params long[] a) => Console.Write(a.Length);
    static public void Test2(this Program p, long[] a) => Console.Write(a.Length);
}

Även i LangVersion=12 eller lägre params måste modifieraren matcha mellan alla metoder för att härleda en unik ombudssignatur. Observera att detta inte påverkar LangVersion=13 och senare på grund av en annan algoritm för delegatinferens.

var d = new C().M; // previously inferred Action<int[]> - now error CS8917: the delegate type could not be inferred

static class E
{
    public static void M(this C c, params int[] x) { }
}

class C
{
    public void M(int[] x) { }
}

En lösning är att använda explicita delegattyper istället för att förlita sig på var inferens i de fallen.

dotnet_style_require_accessibility_modifiers gäller nu konsekvent för gränssnittsmedlemmar

PR: https://github.com/dotnet/roslyn/pull/76324

Före den här ändringen skulle analysatorn för dotnet_style_require_accessibility_modifiers helt enkelt ignorera gränssnittsmedlemmar. Detta berodde på att C# ursprungligen inte tillät modifierare för gränssnittsmedlemmar helt och hållet, eftersom de alltid var offentliga.

Senare versioner av språket lättade på den här begränsningen, vilket gjorde det möjligt för användare att tillhandahålla hjälpmedelsmodifierare för gränssnittsmedlemmar, inklusive en redundant public modifierare.

Analysatorn uppdaterades för att nu framtvinga värdet för det här alternativet även för gränssnittsmedlemmar. Värdets betydelse är följande:

  1. never. Analysatorn utför ingen analys. Redundanta modifierare tillåts för alla medlemmar.
  2. always. Redundanta modifierare krävs alltid för alla medlemmar (inklusive gränssnittsmedlemmar). Till exempel: en private modifierare på en klassmedlem och en public modifierare på en gränssnittsmedlem. Det här är alternativet att använda om du känner att alla medlemmar, oavsett förhållanden, ska ange sin tillgänglighet uttryckligen.
  3. for_non_interface_members. Redundanta modifierare krävs för alla medlemmar som inte ingår i ett gränssnitt, men som inte tillåts för gränssnittsmedlemmar. Till exempel: private krävs för medlemmar i den privata klassen. En medlem i det offentliga gränssnittet får dock inte ha redundanta public modifierare. Detta matchar standardmodifierarsättet som fanns innan språket tillät modifierare på gränssnittselement.
  4. omit_if_default. Redundanta modifierare tillåts inte. En privat klassmedlem kommer till exempel inte att tillåtas att använda private, och en medlem i det offentliga gränssnittet kommer inte att tillåtas att använda public. Det här är alternativet att använda om du anser att det är redundant att upprepa tillgängligheten när den matchar vad språket väljer som standard och det bör inte tillåtas.