Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
In deze zelfstudie leert u wat versiebeheer betekent in .NET. U leert ook welke factoren u moet overwegen bij het versiebeheer van uw bibliotheek en het upgraden naar een nieuwe versie van een bibliotheek.
Taalversie
De C#-compiler maakt deel uit van de .NET SDK. Standaard kiest de compiler de C#-taalversie die overeenkomt met de gekozen TFM voor uw project. Als de SDK-versie groter is dan het gekozen framework, kan de compiler een grotere taalversie gebruiken. U kunt de standaardinstelling wijzigen door het LangVersion element in uw project in te stellen. In ons artikel over compileropties kunt u lezen hoe u dit kunt doen.
Waarschuwing
Het wordt afgeraden om het LangVersion-element op latest in te stellen. De latest instelling betekent dat de geïnstalleerde compiler de nieuwste versie gebruikt. Dat kan veranderen van machine naar machine, waardoor builds onbetrouwbaar zijn. Daarnaast worden taalfuncties ingeschakeld waarvoor runtime- of bibliotheekfuncties mogelijk niet zijn opgenomen in de huidige SDK.
Bibliotheken ontwerpen
Als ontwikkelaar die .NET-bibliotheken heeft gemaakt voor openbaar gebruik, hebt u waarschijnlijk in situaties waarin u nieuwe updates moet implementeren. Hoe u dit proces doet, is veel belangrijk, omdat u ervoor moet zorgen dat er een naadloze overgang van bestaande code naar de nieuwe versie van uw bibliotheek is. Hier volgen verschillende aandachtspunten bij het maken van een nieuwe release:
Semantische versiebeheer
Semantic versioning (SemVer for short) is een naamconventie die wordt toegepast op versies van uw bibliotheek om specifieke mijlpaalgebeurtenissen aan te geven. In het ideale geval moeten de versiegegevens die u uw bibliotheek geeft ontwikkelaars helpen de compatibiliteit met hun projecten te bepalen die gebruikmaken van oudere versies van dezelfde bibliotheek.
De meest eenvoudige benadering van SemVer is de 3-componentindeling MAJOR.MINOR.PATCH, waarbij:
-
MAJORwordt verhoogd wanneer u incompatibele API-wijzigingen aanbrengt -
MINORwordt verhoogd wanneer u functionaliteit toevoegt op een manier die achterwaarts compatibel is -
PATCHwordt verhoogd wanneer u achterwaarts compatibele bugfixes doorvoert
Meer informatie over incrementen van versies met voorbeelden
Hier volgen concrete voorbeelden om te verduidelijken wanneer u elk versienummer wilt verhogen:
Incrementen van PRIMAIRE versies (incompatibele API-wijzigingen)
Deze wijzigingen vereisen dat gebruikers hun code wijzigen om met de nieuwe versie te werken:
Een openbare methode of eigenschap verwijderen:
// Version 1.0.0 public class Calculator { public int Add(int a, int b) => a + b; public int Subtract(int a, int b) => a - b; // This method exists } // Version 2.0.0 - MAJOR increment required public class Calculator { public int Add(int a, int b) => a + b; // Subtract method removed - breaking change! }Methode-signaturen wijzigen:
// Version 1.0.0 public void SaveFile(string filename) { } // Version 2.0.0 - MAJOR increment required public void SaveFile(string filename, bool overwrite) { } // Added required parameterHet gedrag van bestaande methoden wijzigen op manieren die de verwachtingen breken:
// Version 1.0.0 - returns null when file not found public string ReadFile(string path) => File.Exists(path) ? File.ReadAllText(path) : null; // Version 2.0.0 - MAJOR increment required public string ReadFile(string path) => File.ReadAllText(path); // Now throws exception when file not found
KLEINE versieverhogingen (achterwaarts compatibele functionaliteit)
Met deze wijzigingen worden nieuwe functies toegevoegd zonder bestaande code te verbreken:
Nieuwe openbare methoden of eigenschappen toevoegen:
// Version 1.0.0 public class Calculator { public int Add(int a, int b) => a + b; } // Version 1.1.0 - MINOR increment public class Calculator { public int Add(int a, int b) => a + b; public int Multiply(int a, int b) => a * b; // New method added }Nieuwe overbelastingen toevoegen:
// Version 1.0.0 public void Log(string message) { } // Version 1.1.0 - MINOR increment public void Log(string message) { } // Original method unchanged public void Log(string message, LogLevel level) { } // New overload addedOptionele parameters toevoegen aan bestaande methoden:
// Version 1.0.0 public void SaveFile(string filename) { } // Version 1.1.0 - MINOR increment public void SaveFile(string filename, bool overwrite = false) { } // Optional parameterNotitie
Dit is een broncode-compatibele wijziging, maar een binaire wijziging die compatibiliteit breekt. Gebruikers van deze bibliotheek moeten opnieuw compileren om deze correct te laten werken. Veel bibliotheken zouden dit alleen in belangrijke versiewijzigingen overwegen, niet kleine versiewijzigingen.
PATCH-versieverhogingen (achterwaarts compatibele bugfixes)
Met deze wijzigingen worden problemen opgelost zonder nieuwe functies toe te voegen of bestaande functionaliteit te verbreken:
Een fout in de implementatie van een bestaande methode oplossen:
// Version 1.0.0 - has a bug public int Divide(int a, int b) { return a / b; // Bug: doesn't handle division by zero } // Version 1.0.1 - PATCH increment public int Divide(int a, int b) { if (b == 0) throw new ArgumentException("Cannot divide by zero"); return a / b; // Bug fixed, behavior improved but API unchanged }Prestatieverbeteringen die de API niet wijzigen:
// Version 1.0.0 public List<int> SortNumbers(List<int> numbers) { return numbers.OrderBy(x => x).ToList(); // Slower implementation } // Version 1.0.1 - PATCH increment public List<int> SortNumbers(List<int> numbers) { var result = new List<int>(numbers); result.Sort(); // Faster implementation, same API return result; }
Het belangrijkste principe is: als bestaande code uw nieuwe versie zonder wijzigingen kan gebruiken, is dit een MINOR of PATCH-update. Als bestaande code moet worden gewijzigd om met uw nieuwe versie te kunnen werken, is het een MAJOR-update.
Er zijn ook manieren om andere scenario's op te geven, bijvoorbeeld versies van voorlopige versies, bij het toepassen van versiegegevens op uw .NET-bibliotheek.
Compatibiliteit met eerdere versies
Wanneer u nieuwe versies van uw bibliotheek vrijgeeft, is compatibiliteit met eerdere versies waarschijnlijk een van uw belangrijkste problemen. Een nieuwe versie van uw bibliotheek is compatibel met een eerdere versie als code die afhankelijk is van de vorige versie, kan werken met de nieuwe versie wanneer deze opnieuw wordt gecompileerd. Een nieuwe versie van uw bibliotheek is binair compatibel als een toepassing die afhankelijk is van de oude versie, zonder hercompilatie, met de nieuwe versie kan werken.
Hier volgen enkele aandachtspunten bij het behouden van compatibiliteit met eerdere versies van uw bibliotheek:
- Virtuele methoden: wanneer u een virtuele methode maakt die niet virtueel is in uw nieuwe versie, betekent dit dat projecten die deze methode overschrijven, moeten worden bijgewerkt. Dit is een enorme belangrijke wijziging en wordt sterk afgeraden.
- Wanneer het bijwerken van het gedrag van een methode u dwingt om ook de signatuur te veranderen, is het beter om een overload te creëren zodat code die deze methode aanroept, nog steeds functioneert. U kunt altijd de oude methodehandtekening manipuleren om de nieuwe methodehandtekening aan te roepen, zodat de implementatie consistent blijft.
- Verouderd kenmerk: u kunt dit kenmerk in uw code gebruiken om klassen of klasseleden op te geven die zijn afgeschaft en die waarschijnlijk in toekomstige versies worden verwijderd. Dit zorgt ervoor dat ontwikkelaars die gebruikmaken van uw bibliotheek beter zijn voorbereid op brekende wijzigingen.
- Optionele methodeargumenten: wanneer u eerder optionele methodeargumenten verplicht maakt of de standaardwaarde wijzigt, moet alle code die deze argumenten niet levert, worden bijgewerkt.
Notitie
Het optioneel maken van verplichte argumenten moet zeer weinig effect hebben, met name als het gedrag van de methode niet wordt gewijzigd.
Hoe eenvoudiger u het voor uw gebruikers maakt om een upgrade uit te voeren naar de nieuwe versie van uw bibliotheek, hoe groter de kans dat ze een upgrade uitvoeren.
Toepassingsconfiguratiebestand
Als .NET-ontwikkelaar is de kans zeer groot dat je het bestand dat app.config aanwezig is in de meeste projecttypen bent tegengekomen.
Dit eenvoudige configuratiebestand kan een lange weg gaan naar het verbeteren van de implementatie van nieuwe updates. Over het algemeen moet u uw bibliotheken zodanig ontwerpen dat informatie die waarschijnlijk regelmatig wordt gewijzigd, in het app.config bestand wordt opgeslagen, op deze manier wanneer dergelijke informatie wordt bijgewerkt, het configuratiebestand van oudere versies alleen moet worden vervangen door het nieuwe bestand zonder dat de bibliotheek opnieuw hoeft te worden gecompileerd.
Bibliotheken gebruiken
Als ontwikkelaar die .NET-bibliotheken gebruikt die zijn gebouwd door andere ontwikkelaars, weet u waarschijnlijk dat een nieuwe versie van een bibliotheek mogelijk niet volledig compatibel is met uw project en dat u uw code vaak moet bijwerken om met deze wijzigingen te werken.
Gelukkig voor u, C# en het .NET-ecosysteem worden geleverd met functies en technieken waarmee we onze app eenvoudig kunnen bijwerken om te werken met nieuwe versies van bibliotheken die belangrijke wijzigingen kunnen veroorzaken.
Omleiding van assemblybinding
U kunt het bestand app.config gebruiken om de versie van een bibliotheek die door uw app wordt gebruikt, bij te werken. Door een bindingsomleiding toe te voegen, kunt u de nieuwe bibliotheekversie gebruiken zonder dat u uw app opnieuw hoeft te compileren. In het volgende voorbeeld ziet u hoe u het bestand app.config van uw app bijwerkt om de 1.0.1 patchversie van ReferencedLibrary te gebruiken in plaats van de 1.0.0 versie waarmee het oorspronkelijk was gecompileerd.
<dependentAssembly>
<assemblyIdentity name="ReferencedLibrary" publicKeyToken="32ab4ba45e0a69a1" culture="en-us" />
<bindingRedirect oldVersion="1.0.0" newVersion="1.0.1" />
</dependentAssembly>
Notitie
Deze aanpak werkt alleen als de nieuwe versie van ReferencedLibrary binair compatibel is met uw app.
Zie de sectie Compatibiliteit met eerdere versies hierboven voor wijzigingen waar u op moet letten bij het bepalen van de compatibiliteit.
Nieuw
U gebruikt de new wijzigingsfunctie om overgenomen leden van een basisklasse te verbergen. Dit is een manier waarop afgeleide klassen kunnen reageren op updates in basisklassen.
Neem het volgende voorbeeld:
public class BaseClass
{
public void MyMethod()
{
Console.WriteLine("A base method");
}
}
public class DerivedClass : BaseClass
{
public new void MyMethod()
{
Console.WriteLine("A derived method");
}
}
public static void Main()
{
BaseClass b = new BaseClass();
DerivedClass d = new DerivedClass();
b.MyMethod();
d.MyMethod();
}
Uitvoer
A base method
A derived method
In het bovenstaande voorbeeld kun je zien hoe DerivedClass de methode MyMethod die in BaseClass aanwezig is, verbergt.
Dit betekent dat wanneer een basisklasse in de nieuwe versie van een bibliotheek een lid toevoegt dat al bestaat in uw afgeleide klasse, u de new wijzigingsfunctie op het afgeleide klasselid kunt gebruiken om het lid van de basisklasse te verbergen.
Wanneer er geen new wijzigingsfunctie is opgegeven, verbergt een afgeleide klasse standaard conflicterende leden in een basisklasse, hoewel er een compilerwaarschuwing wordt gegenereerd, wordt de code nog steeds gecompileerd. Dit betekent dat het toevoegen van nieuwe leden aan een bestaande klasse ervoor zorgt dat die nieuwe versie van uw bibliotheek zowel bron als binair compatibel is met code die ervan afhankelijk is.
overschrijven
De override wijzigingsfunctie betekent dat een afgeleide implementatie de implementatie van een basisklasselid uitbreidt in plaats van deze te verbergen. Het lid van de basisklasse moet de virtual wijzigingsfunctie erop toepassen.
public class MyBaseClass
{
public virtual string MethodOne()
{
return "Method One";
}
}
public class MyDerivedClass : MyBaseClass
{
public override string MethodOne()
{
return "Derived Method One";
}
}
public static void Main()
{
MyBaseClass b = new MyBaseClass();
MyDerivedClass d = new MyDerivedClass();
Console.WriteLine($"Base Method One: {b.MethodOne()}");
Console.WriteLine($"Derived Method One: {d.MethodOne()}");
}
Uitvoer
Base Method One: Method One
Derived Method One: Derived Method One
De override wijzigingsfunctie wordt geëvalueerd tijdens het compileren en de compiler genereert een fout als er geen virtueel lid wordt gevonden dat moet worden overschreven.
Uw kennis van de besproken technieken en uw begrip van de situaties waarin u deze kunt gebruiken, zal een lange weg gaan naar het versoepelen van de overgang tussen versies van een bibliotheek.