Změna pravidel pro zajištění kompatibility
V průběhu své historie se rozhraní .NET pokusilo zachovat vysokou úroveň kompatibility od verze po verzi a napříč implementacemi .NET. I když se .NET 5 (a .NET Core) a novější verze dají považovat za novou technologii ve srovnání s rozhraním .NET Framework, dvě hlavní faktory omezují schopnost této implementace rozhraní .NET odcházet od rozhraní .NET Framework:
Velký počet vývojářů, kteří původně vyvinuli nebo pokračovali v vývoji aplikací rozhraní .NET Framework. Očekávají konzistentní chování napříč implementacemi .NET.
Projekty knihoven .NET Standard umožňují vývojářům vytvářet knihovny, které cílí na běžná rozhraní API sdílená rozhraním .NET Framework a .NET 5 (a .NET Core) a novějšími verzemi. Vývojáři očekávají, že knihovna používaná v aplikaci .NET 5 by se měla chovat stejně jako stejná knihovna používaná v aplikaci .NET Framework.
Spolu s kompatibilitou napříč implementacemi .NET očekávají vývojáři vysokou úroveň kompatibility napříč verzemi dané implementace .NET. Kód napsaný pro starší verzi .NET Core by se měl bez problémů spouštět v .NET 5 nebo novější verzi. Ve skutečnosti mnoho vývojářů očekává, že nová rozhraní API nalezená v nově vydaných verzích rozhraní .NET by měla být také kompatibilní s předběžnými verzemi, ve kterých byla tato rozhraní API zavedena.
Tento článek popisuje změny, které ovlivňují kompatibilitu a způsob, jakým tým .NET vyhodnocuje jednotlivé typy změn. Pochopení toho, jak tým .NET přistupuje k možným zásadním změnám, je užitečný zejména pro vývojáře, kteří otevírají žádosti o přijetí změn, které upravují chování stávajících rozhraní .NET API.
Následující části popisují kategorie změn provedených v rozhraních .NET API a jejich dopad na kompatibilitu aplikací. Změny jsou buď povolené (), nepovolené (✔️❌), nebo vyžadují úsudek a vyhodnocení toho, jak předvídatelné, zřejmé a konzistentní bylo předchozí chování (❓).
Poznámka:
- Kromě toho, že slouží jako průvodce vyhodnocováním změn knihoven .NET, mohou vývojáři knihoven tato kritéria použít také k vyhodnocení změn knihoven, které cílí na více implementací a verzí .NET.
- Informace o kategoriích kompatibility, například dopředné a zpětné kompatibility, naleznete v tématu Jak můžou změny kódu ovlivnit kompatibilitu.
Úpravy veřejné zakázky
Změny v této kategorii upravují veřejnou plochu typu. Většina změn v této kategorii je zakázána, protože porušují zpětnou kompatibilitu (schopnost aplikace vyvinuté s předchozí verzí rozhraní API ke spuštění bez rekompilace u novější verze).
Typy
✔️ POVOLENO: Odebrání implementace rozhraní z typu, pokud rozhraní je již implementováno základním typem
❓ VYŽADUJE ÚSUDEK: Přidání nové implementace rozhraní do typu
Jedná se o přijatelnou změnu, protože nemá nepříznivý vliv na stávající klienty. Všechny změny typu musí fungovat v rámci hranic přijatelných změn definovaných zde, aby nová implementace zůstala přijatelná. Při přidávání rozhraní, která přímo ovlivňují schopnost návrháře nebo serializátoru generovat kód nebo data, která nelze využívat nižší úroveň, je nutné velmi opatrně. Příkladem je ISerializable rozhraní.
❓ VYŽADUJE ÚSUDEK: Představujeme novou základní třídu.
Typ lze zavést do hierarchie mezi dvěma existujícími typy, pokud nezavádějí žádné nové abstraktní členy nebo nemění sémantiku nebo chování existujících typů. Například v rozhraní .NET Framework 2.0 DbConnection se třída stala novou základní třídou , SqlConnectionkterá dříve odvozena přímo z Component.
✔️ POVOLENO: Přesunutí typu z jednoho sestavení do druhého
Staré sestavení musí být označeno odkazem TypeForwardedToAttribute na nové sestavení.
✔️ POVOLENO: Změna typu struktury na
readonly struct
typreadonly struct
Změna typu nastruct
typ není povolená.✔️ POVOLENO: Přidání zapečetěného nebo abstraktního klíčového slova do typu, pokud nejsou k dispozici žádné přístupné (veřejné nebo chráněné) konstruktory
✔️ POVOLENO: Rozšíření viditelnosti typu
❌DISALLOWED: Změna oboru názvů nebo názvu typu
❌ZAKÁZÁNO: Přejmenování nebo odebrání veřejného typu
Tím se přeruší veškerý kód, který používá přejmenovaný nebo odebraný typ.
Poznámka:
Ve výjimečných případech může .NET odebrat veřejné rozhraní API. Další informace najdete v tématu Odebrání rozhraní API v .NET. Informace o . Zásady podpory net najdete v tématu Zásady podpory .NET.
❌DISALLOWED: Změna základního typu výčtu
Jedná se o změnu času kompilace a porušení chování a binární zásadní změnu, která může znepřístupnit argumenty atributů.
❌ZAKÁZÁNO: Zapečetění typu, který byl dříve nezapečetěn
❌DISALLOWED: Přidání rozhraní do sady základních typů rozhraní
Pokud rozhraní implementuje rozhraní, které dříve neimplementoval, všechny typy, které implementovaly původní verzi rozhraní, jsou přerušeny.
❓ VYŽADUJE ÚSUDEK: Odebrání třídy ze sady základních tříd nebo rozhraní ze sady implementovaných rozhraní
Existuje jedna výjimka pravidla pro odebrání rozhraní: můžete přidat implementaci rozhraní, které je odvozeno z odebraného rozhraní. Můžete například odebrat IDisposable , pokud typ nebo rozhraní nyní implementuje IComponent, který implementuje IDisposable.
❌DISALLOWED: Změna
readonly struct
typu na typ strukturyZměna
struct
typureadonly struct
na typ je však povolená.❌DISALLOWED: Změna typu struktury na
ref struct
typ a naopak❌ZAKÁZÁNO: Snížení viditelnosti typu
Zvýšení viditelnosti typu je však povoleno.
Členové
✔️ POVOLENO: Rozšíření viditelnosti člena, který není virtuální
✔️ POVOLENO: Přidání abstraktního členu do veřejného typu, který nemá přístupné (veřejné nebo chráněné) konstruktory nebo je typ zapečetěn
Přidání abstraktního členu do typu, který má přístupné (veřejné nebo chráněné) konstruktory a není
sealed
povolený.✔️ POVOLENO: Omezení viditelnosti chráněného členu, pokud typ nemá přístupné (veřejné nebo chráněné) konstruktory nebo je typ zapečetěn.
✔️ POVOLENO: Přesunutí člena do vyšší třídy v hierarchii než typ, ze kterého byl odebrán
✔️ POVOLENO: Přidání nebo odebrání přepsání
Zavedení přepsání může způsobit, že předchozí příjemci přeskočili přepsání při volání základu.
✔️ POVOLENO: Přidání konstruktoru do třídy spolu s konstruktorem bez parametrů, pokud třída dříve neměla žádné konstruktory
Přidání konstruktoru do třídy, která dříve neměla žádné konstruktory bez přidání konstruktoru bez parametru není povolena.
✔️ POVOLENO: Změna člena z abstraktního na virtuální
✔️ POVOLENO: Změna z
ref readonly
hodnoty na návratovouref
hodnotu (s výjimkou virtuálních metod nebo rozhraní)✔️ POVOLENO: Odebrání jen pro čtení z pole, pokud statický typ pole není proměnlivým typem hodnoty
✔️ POVOLENO: Volání nové události, která nebyla dříve definována
❓ VYŽADUJE ROZSUDEK: Přidání nového pole instance do typu
Tato změna má vliv na serializaci.
❌DISALLOWED: Přejmenování nebo odebrání veřejného člena nebo parametru
Tím se přeruší veškerý kód, který používá přejmenovaný nebo odebraný člen nebo parametr.
To zahrnuje odebrání nebo přejmenování getteru nebo setter z vlastnosti a také přejmenování nebo odebrání členů výčtu.
❌ZAKÁZÁNO: Přidání člena do rozhraní
Pokud zadáte implementaci, přidání nového člena do existujícího rozhraní nemusí nutně vést k selhání kompilace v podřízených sestaveních. Ne všechny jazyky však podporují výchozí členy rozhraní (DIM). V některých scénářích se modul runtime také nemůže rozhodnout, který výchozí člen rozhraní se má vyvolat. Z těchto důvodů se přidání člena do existujícího rozhraní považuje za zásadní změnu.
❌ZAKÁZÁNO: Změna hodnoty veřejné konstanty nebo člena výčtu
❌ZAKÁZÁNO: Změna typu vlastnosti, pole, parametru nebo návratové hodnoty
❌ZAKÁZÁNO: Přidání, odebrání nebo změna pořadí parametrů
❌ZAKÁZÁNO: Přidání nebo odebrání klíčového slova in, out nebo ref z parametru
❌DISALLOWED: Přejmenování parametru (včetně změny jejího případu)
To se považuje za zásadní ze dvou důvodů:
Přeruší zpožděné scénáře, jako je funkce pozdní vazby v jazyce Visual Basic a dynamická v jazyce C#.
Přeruší kompatibilitu zdroje, když vývojáři používají pojmenované argumenty.
❌ZAKÁZÁNO: Změna z návratové
ref
hodnoty na návratovouref readonly
hodnotu❌️ NEPOVOLENÉ: Změna hodnoty na
ref readonly
ref
návratovou hodnotu pro virtuální metodu nebo rozhraní❌ZAKÁZÁNO: Přidání nebo odebrání abstrakce ze člena
❌ZAKÁZÁNO: Odebrání virtuálního klíčového slova ze člena
❌ZAKÁZÁNO: Přidání virtuálního klíčového slova do člena
I když to často není zásadní změna, protože kompilátor jazyka C# má tendenci generovat instrukce callvirt Intermediate Language (IL) pro volání nevirtuální metody (
callvirt
provádí kontrolu null, zatímco normální volání není), toto chování není z několika důvodů neměnné:Jazyk C# není jediným jazykem, který cílí .NET.
Kompilátor jazyka C# se stále častěji snaží optimalizovat
callvirt
normální volání vždy, když cílová metoda není virtuální a pravděpodobně nemá hodnotu null (například metodu, ke které se přistupuje prostřednictvím operátoru šíření ?. null).
Vytvoření metody virtuální znamená, že kód příjemce by ho často volal ne virtuálně.
❌ZAKÁZÁNO: Vytvoření abstraktního virtuálního člena
Virtuální člen poskytuje implementaci metody, kterou lze přepsat odvozenou třídou. Abstraktní člen neposkytuje žádnou implementaci a musí být přepsán.
❌ZAKÁZÁNO: Přidání zapečetěného klíčového slova do člena rozhraní
Přidání
sealed
do výchozího členu rozhraní způsobí, že není virtuální, což brání volání implementace odvozeného typu daného člena.❌ZAKÁZÁNO: Přidání abstraktního člena do veřejného typu, který má přístupné (veřejné nebo chráněné) konstruktory a který není zapečetěn
❌ZAKÁZÁNO: Přidání nebo odebrání statického klíčového slova ze člena
❌DISALLOWED: Přidání přetížení, které brání existující přetížení a definuje jiné chování
Tím dojde k přerušení stávajících klientů, kteří byli vázáni na předchozí přetížení. Pokud má například třída jednu verzi metody, která přijímá UInt32, stávající příjemce úspěšně vytvoří vazbu na toto přetížení při předání Int32 hodnoty. Pokud však přidáte přetížení, které přijímá Int32, při rekompilování nebo použití pozdní vazby, kompilátor nyní vytvoří vazbu na nové přetížení. Pokud se liší výsledky chování, jedná se o zásadní změnu.
❌DISALLOWED: Přidání konstruktoru do třídy, která dříve neměla žádný konstruktor bez přidání konstruktoru bez parametru
❌️ NEPOVOLENÉ: Přidání jen pro čtení do pole
❌ZAKÁZÁNO: Snížení viditelnosti člena
To zahrnuje snížení viditelnosti chráněného členu, pokud jsou dostupné (
public
neboprotected
) konstruktory a typ není zapečetěn. Pokud tomu tak není, omezení viditelnosti chráněného člena je povoleno.Zvýšení viditelnosti člena je povoleno.
❌ZAKÁZÁNO: Změna typu člena
Návratovou hodnotu metody nebo typu vlastnosti nebo pole nelze změnit. Například podpis metody, která vrací Object nelze změnit, aby vrátila Stringhodnotu , nebo naopak.
❌DISALLOWED: Přidání pole instance do struktury, která neobsahuje žádná nepubliková pole
Pokud struktura obsahuje pouze veřejná pole nebo nemá vůbec žádná pole, volající mohou deklarovat místní hodnoty tohoto typu struktury bez volání konstruktoru struktury nebo první inicializace místního na
default(T)
, pokud jsou všechna veřejná pole nastavena na strukturu před prvním použitím. Přidání všech nových polí – veřejných nebo nepublikovaných – do takové struktury je změna způsobující chybu zdroje pro tyto volající, protože kompilátor teď bude vyžadovat inicializaci dalších polí.Přidání všech nových polí – veřejných nebo nepublikovaných – do struktury bez polí nebo pouze veřejných polí je binární zásadní změna volajících, kteří použili
[SkipLocalsInit]
na kód. Vzhledem k tomu, že kompilátor nevěděl o těchto polích v době kompilace, mohl vygenerovat il, který plně inicializuje strukturu, což vede k vytvoření struktury z neinicializovaných dat zásobníku.Pokud má struktura jakákoli nepublikovaná pole, kompilátor již vynucuje inicializaci prostřednictvím konstruktoru nebo
default(T)
a přidání nových polí instance není zásadní změnou.❌ZAKÁZÁNO: Spuštění existující události, když se nikdy neaktivovalo
Změny chování
Sestavení
✔️ POVOLENO: Přenosná sestavení, pokud jsou stále podporovány stejné platformy
❌ZAKÁZÁNO: Změna názvu sestavení
❌ZAKÁZÁNO: Změna veřejného klíče sestavení
Vlastnosti, pole, parametry a návratové hodnoty
✔️ POVOLENO: Změna hodnoty vlastnosti, pole, návratové hodnoty nebo out parametru na odvozenější typ
Například metoda, která vrací typ Object může vrátit String instanci. (Podpis metody se však nemůže změnit.)
✔️ POVOLENO: Zvýšení rozsahu přijatých hodnot pro vlastnost nebo parametr, pokud člen není virtuální
Zatímco rozsah hodnot, které lze předat metodě nebo jsou vráceny člen může rozbalit, parametr nebo typ členu nemůže. Například zatímco hodnoty předané metodě mohou rozšířit z 0-124 na 0-255, typ parametru se nemůže změnit z Byte na Int32.
❌DISALLOWED: Zvýšení rozsahu přijatých hodnot pro vlastnost nebo parametr, pokud je člen virtuální
Tato změna přeruší stávající přepsané členy, které nebudou správně fungovat pro rozšířený rozsah hodnot.
❌DISALLOWED: Snížení rozsahu přijatých hodnot pro vlastnost nebo parametr
❌DISALLOWED: Zvýšení rozsahu vrácených hodnot pro vlastnost, pole, návratovou hodnotu nebo out parametr
❌DISALLOWED: Změna vrácených hodnot pro vlastnost, pole, návratovou hodnotu metody nebo out parametr
❌DISALLOWED: Změna výchozí hodnoty vlastnosti, pole nebo parametru
Změna nebo odebrání výchozí hodnoty parametru není binární konec. Odebrání výchozí hodnoty parametru je zdrojový konec a změna výchozí hodnoty parametru může vést k narušení chování po rekompilace.
Z tohoto důvodu je odebrání výchozích hodnot parametru přijatelné v konkrétním případě "přesunutí" těchto výchozích hodnot do nového přetížení metody, aby se eliminovala nejednoznačnost. Představte si například existující metodu
MyMethod(int a = 1)
. Pokud zavádíte přetíženíMyMethod
se dvěma volitelnými parametrya
ab
můžete zachovat kompatibilitu přesunutím výchozí hodnotya
do nového přetížení. Nyní jsou dvě přetíženíMyMethod(int a)
aMyMethod(int a = 1, int b = 2)
. Tento model umožňujeMyMethod()
kompilaci.❌ZAKÁZÁNO: Změna přesnosti číselné návratové hodnoty
❓ VYŽADUJE ROZSUDEK: Změna analýzy vstupu a vyvolání nových výjimek (i když není v dokumentaci uvedeno chování analýzy
Výjimky
✔️ POVOLENO: Vyvolání odvozené výjimky než existující výjimka
Vzhledem k tomu, že nová výjimka je podtřídou existující výjimky, předchozí kód zpracování výjimek bude nadále zpracovávat výjimku. Například v rozhraní .NET Framework 4 se metody vytváření a načítání jazykové verze začaly hodit CultureNotFoundException místo ArgumentException toho, zda nelze najít jazykovou verzi. Vzhledem k tomu CultureNotFoundException , že se odvozuje z ArgumentException, je to přijatelná změna.
✔️ POVOLENO: Vyvolání konkrétnější výjimky než NotSupportedException, NotImplementedExceptionNullReferenceException
✔️ POVOLENO: Vyvolání výjimky, která je považována za neobnovitelnou
Neopravitelné výjimky by neměly být zachyceny, ale místo toho by se měly zpracovávat obslužnou rutinou pro zachycení na vysoké úrovni. Uživatelé proto nemají kód, který tyto explicitní výjimky zachytí. Neopravitelné výjimky jsou:
✔️ POVOLENO: Vyvolání nové výjimky v nové cestě kódu
Výjimka se musí vztahovat pouze na novou cestu kódu, která se spouští s novými hodnotami nebo stavem parametrů a která nemůže být spuštěna existujícím kódem, který cílí na předchozí verzi.
✔️ POVOLENO: Odebrání výjimky pro povolení robustnějšího chování nebo nových scénářů
Například metodu
Divide
, která dříve zpracovávala pouze kladné hodnoty a hodila ArgumentOutOfRangeException jinak, lze změnit tak, aby podporovala záporné i kladné hodnoty bez vyvolání výjimky.✔️ POVOLENO: Změna textu chybové zprávy
Vývojáři by se neměli spoléhat na text chybových zpráv, které se také mění v závislosti na jazykové verzi uživatele.
❌ZAKÁZÁNO: Vyvolání výjimky v jiném případě, který není uvedený výše
❌ZAKÁZÁNO: Odebrání výjimky v jiném případě, který není uveden výše
Atributy
✔️ POVOLENO: Změna hodnoty atributu, který není pozorovatelný
❌ZAKÁZÁNO: Změna hodnoty atributu, který je pozorovatelný
❓ VYŽADUJE ÚSUDEK: Odebrání atributu
Ve většině případů je odebrání atributu (například NonSerializedAttribute) zásadní změnou.
Podpora platformy
✔️ POVOLENO: Podpora operace na platformě, která se dříve nepodporovala
❌ZAKÁZÁNO: Nepodporuje nebo nyní vyžaduje konkrétní aktualizaci Service Pack pro operaci, která byla dříve podporována na platformě.
Změny interní implementace
❓ VYŽADUJE ÚSUDEK: Změna plochy vnitřního typu
Tyto změny jsou obecně povoleny, i když přerušují soukromé reflexe. V některých případech, kdy oblíbené knihovny třetích stran nebo velký počet vývojářů závisí na interních rozhraních API, nemusí být tyto změny povolené.
❓ VYŽADUJE ROZSUDEK: Změna interního provádění člena
Tyto změny jsou obecně povolené, i když přerušují soukromé reflexe. V některýchpřípadechch kódech často závisí na soukromé reflexi nebo na tom, kde změna představuje nežádoucí vedlejší účinky, nemusí být tyto změny povolené.
✔️ POVOLENO: Zlepšení výkonu operace
Schopnost upravit výkon operace je nezbytná, ale takové změny můžou přerušit kód, který závisí na aktuální rychlosti operace. To platí zejména pro kód, který závisí na načasování asynchronních operací. Změna výkonu by neměla mít žádný vliv na jiné chování příslušného rozhraní API; jinak se změna přeruší.
✔️ POVOLENO: Nepřímo (a často nepříznivě) mění výkon operace.
Pokud změna v otázce není zařazena do kategorií z nějakého jiného důvodu, je to přijatelné. Často je potřeba provést akce, které můžou zahrnovat další operace nebo které přidávají nové funkce. To bude téměř vždy mít vliv na výkon, ale může být nezbytné, aby příslušné rozhraní API fungovalo podle očekávání.
❌DISALLOWED: Změna synchronního rozhraní API na asynchronní (a naopak)
Změny kódu
✔️ POVOLENO: Přidání parametrů do parametru
❌ZAKÁZÁNO: Přidání kontrolovaného klíčového slova do bloku kódu
Tato změna může způsobit, že kód, který se dříve spustil, vyvolá OverflowException a je nepřijatelný.
❌DISALLOWED: Odebrání parametrů z parametru
❌ZAKÁZÁNO: Změna pořadí, ve kterém se události aktivují
Vývojáři můžou přiměřeně očekávat, že se události aktivují ve stejném pořadí a kód vývojáře často závisí na pořadí, ve kterém se události aktivují.
❌ZAKÁZÁNO: Odebrání vyvolání události u dané akce
❌ZAKÁZÁNO: Změna počtu zadaných událostí se nazývá
❌DISALLOWED: Přidání do typu výčtu FlagsAttribute