Versions- och uppdateringsöverväganden för C#-utvecklare

Kompatibilitet är ett viktigt mål eftersom nya funktioner läggs till i C#-språket. I nästan alla fall kan befintlig kod kompileras om med en ny kompilatorversion utan problem. .NET-körningsteamet har också ett mål att säkerställa kompatibilitet för uppdaterade bibliotek. I nästan alla fall, när din app startas från en uppdaterad körning med uppdaterade bibliotek, är beteendet exakt detsamma som med tidigare versioner.

Den språkversion som används för att kompilera din app matchar vanligtvis runtime target framework moniker (TFM) som refereras i projektet. Mer information om hur du ändrar standardspråkversionen finns i artikeln om hur du konfigurerar språkversionen. Det här standardbeteendet garanterar maximal kompatibilitet.

När icke-bakåtkompatibla ändringar introduceras klassificeras de som:

  • Binär icke-bakåtkompatibel ändring: En binär icke-bakåtkompatibel ändring orsakar olika beteende, inklusive eventuellt krascher, i ditt program eller bibliotek när den startas med hjälp av en ny körning. Du måste kompilera om din app för att införliva dessa ändringar. Den befintliga binärfilen fungerar inte korrekt.
  • Källbrytande ändring: En källbrytande ändring ändrar innebörden av källkoden. Du måste göra källkodsredigeringar innan du kompilerar programmet med den senaste språkversionen. Din befintliga binärfil körs korrekt med den nyare värden och körningen. Observera att för språksyntax är en källbrytande ändring också en beteendeförändring, enligt definitionen i ändringarna som bryter mot körningen.

När en binär icke-bakåtkompatibel ändring påverkar din app måste du kompilera om din app, men du behöver inte redigera någon källkod. När en källbrytande ändring påverkar din app körs den befintliga binära filen fortfarande korrekt i miljöer med den uppdaterade körningen och biblioteken. Du måste dock göra källändringar för att kompilera om med den nya språkversionen och körningen. Om en ändring är både källbrytande och binär brytning måste du kompilera om programmet med den senaste versionen och göra källuppdateringar.

På grund av målet att undvika icke-bakåtkompatibla ändringar av C#-språkteamet och körningsteamet, handlar det vanligtvis om att uppdatera TFM och återskapa appen. För bibliotek som distribueras offentligt bör du dock noggrant utvärdera din princip för TFM:er som stöds och språkversioner som stöds. Du kanske skapar ett nytt bibliotek med funktioner som finns i den senaste versionen och måste se till att appar som skapats med tidigare versioner av kompilatorn kan använda det. Eller så kanske du uppgraderar ett befintligt bibliotek och många av dina användare kanske inte har uppgraderat versioner ännu.

Introduktion till icke-bakåtkompatibla ändringar i dina bibliotek

När du använder nya språkfunktioner i bibliotekets offentliga API bör du utvärdera om införandet av funktionen medför antingen en binär ändring eller källbrytande ändring för biblioteksanvändare. Alla ändringar i din interna implementering som inte visas i gränssnitten public eller protected är kompatibla.

Kommentar

Om du använder System.Runtime.CompilerServices.InternalsVisibleToAttribute för att aktivera typer för att se interna medlemmar kan de interna medlemmarna införa icke-bakåtkompatibla ändringar.

En binär icke-bakåtkompatibel ändring kräver att användarna kompilera om sin kod för att kunna använda den nya versionen. Tänk till exempel på den här offentliga metoden:

public double CalculateSquare(double value) => value * value;

Om du lägger till in modifieraren i metoden är det en binär icke-bakåtkompatibel ändring:

public double CalculateSquare(in double value) => value * value;

Användarna måste kompilera om alla program som använder CalculateSquare metoden för att det nya biblioteket ska fungera korrekt.

En icke-bakåtkompatibel källändring kräver att användarna ändrar sin kod innan de kompileras om. Tänk till exempel på den här typen:

public class Person
{
    public string FirstName { get; }
    public string LastName { get; }

    public Person(string firstName, string lastName) => (FirstName, LastName) = (firstName, lastName);

    // other details omitted
}

I en nyare version vill du dra nytta av de syntetiserade medlemmar som genereras för record typer. Du gör följande ändring:

public record class Person(string FirstName, string LastName);

Den tidigare ändringen kräver ändringar för alla typer som härletts från Person. Alla dessa deklarationer måste lägga till modifieraren i record sina deklarationer.

Påverkan av icke-bakåtkompatibla ändringar

När du lägger till en binär icke-bakåtkompatibel ändring i biblioteket tvingar du alla projekt som använder biblioteket att kompilera om. Ingen av källkoden i dessa projekt behöver dock ändras. Därför är effekten av den icke-bakåtkompatibla förändringen ganska liten för varje projekt.

När du gör en källändring i biblioteket måste alla projekt göra källändringar för att kunna använda det nya biblioteket. Om den nödvändiga ändringen kräver nya språkfunktioner tvingar du projekten att uppgradera till samma språkversion och TFM som du nu använder. Du har krävt mer arbete för dina användare och eventuellt tvingat dem att uppgradera också.

Effekten av eventuella icke-bakåtkompatibla ändringar beror på antalet projekt som är beroende av biblioteket. Om biblioteket används internt av några få program kan du reagera på eventuella icke-bakåtkompatibla ändringar i alla projekt som påverkas. Men om biblioteket laddas ned offentligt bör du utvärdera den potentiella effekten och överväga alternativ:

  • Du kan lägga till nya API:er som parallella befintliga API:er.
  • Du kan överväga parallella versioner för olika TFM:er.
  • Du kan överväga flera mål.