A definitive guide to API-breaking changes in.NET
Chciałbym zebrać jak najwięcej informacji na temat wersjonowania API w. NET / CLR, a konkretnie jak zmiany API powodują lub nie łamią aplikacji klienckich. Najpierw zdefiniujmy niektóre terminy:
Zmiana API - Zmiana publicznie widocznej definicji typu, w tym dowolnego z jego publicznych członków. Obejmuje to zmianę typu i nazw członów, zmianę typu bazowego typu, dodawanie / usuwanie interfejsów z listy zaimplementowanych interfejsów typu, dodawanie / usuwanie elementów składowych( w tym przeciążeń), zmiana widoczności elementów składowych, zmiana nazw parametrów metod i typów, dodawanie domyślnych wartości parametrów metod, dodawanie / usuwanie atrybutów typów i elementów składowych oraz dodawanie / usuwanie ogólnych parametrów typów i elementów składowych (czy coś przegapiłem?). Nie obejmuje to żadnych zmian w organach członkowskich ani żadnych zmian w członkach prywatnych(tzn. nie bierzemy pod uwagę refleksji).
Binary-level break - Zmiana API, która powoduje to, że zestawy klienckie skompilowane ze starszą wersją API mogą nie ładować się z nową wersją. Przykład: zmiana podpisu metody, nawet jeśli pozwala na wywołanie w taki sam sposób jak poprzednio (np.: void, aby zwracać przeciążenia domyślnych wartości typu / parametru).
Source-level break - Zmiana API, która powoduje, że istniejący kod napisany do kompilacji ze starszą wersją API może nie kompilować się z nową wersją. Już skompilowane zespoły klientów jednak działa jak poprzednio. Przykład: dodanie nowego przeciążenia, które może spowodować niejednoznaczność w wywołaniach metod, które były wcześniej jednoznaczne.
Quiet semantics change - Zmiana API, która powoduje, że istniejący kod napisany do kompilacji ze starszą wersją API po cichu zmienia jego semantykę, np. przez wywołanie innej metody. Kod powinien jednak nadal kompilować się bez ostrzeżeń/błędów, a wcześniej skompilowane zespoły powinny działać jak wcześniej. Przykład: implementacja nowego interfejsu na istniejącej klasie, który powoduje wybranie innego przeciążenia podczas rozwiązywania przeciążeń.
Ostatecznym celem jest skatalogowanie jak największej liczby łamających się i cichych zmian w API semantyki oraz opisanie dokładnego efektu łamania oraz języków, na które nie ma to wpływu. Aby rozszerzyć na ten ostatni: podczas gdy niektóre zmiany wpływają na wszystkie języki powszechnie (np. dodanie nowego członka do interfejsu złamie implementacje tego interfejsu w każdy język), niektóre wymagają bardzo specyficznej semantyki języka, aby wejść w grę, aby uzyskać przerwę. Najczęściej wiąże się to z przeciążeniem metody i, ogólnie rzecz biorąc, wszystkim, co ma związek z niejawnymi konwersjami typu. Wydaje się, że nie ma sposobu na zdefiniowanie "najmniej wspólnego mianownika" nawet dla języków zgodnych z CLS (tj. tych zgodnych co najmniej z regułami "CLS consumer" zdefiniowanymi w CLI spec) - chociaż będę wdzięczny, jeśli ktoś poprawi mnie jako błąd tutaj - więc to będzie język po języku. Najbardziej interesujące są oczywiście te, które są dostarczane z. NET po wyjęciu z pudełka: C#, VB i F#; ale inne, takie jak IronPython, IronRuby, Delphi Prism itp. Im bardziej jest to przypadek narożny, tym bardziej będzie to interesujące - rzeczy takie jak usuwanie członków są dość oczywiste, ale subtelne interakcje między np. przeciążeniem metody,opcjonalnymi / domyślnymi parametrami, wnioskowaniem typu lambda i operatorami konwersji mogą być bardzo zaskakujące przy razy.
Kilka przykładów na rozpoczęcie tego:
Dodawanie nowych przeciążeń metody
Kind: source-level break
Języki, których dotyczy problem: C#, VB, F#
API przed zmianą:
public class Foo
{
public void Bar(IEnumerable x);
}
API po zmianie:
public class Foo
{
public void Bar(IEnumerable x);
public void Bar(ICloneable x);
}
Przykładowy kod klienta działający przed zmianą i uszkodzony po niej:
new Foo().Bar(new int[0]);
Dodanie nowego implicit conversion operator overloads
Kind: Source-level break.
Języki, których dotyczy problem: C#, VB
Języki bez wpływu: F #
API przed zmianą:
public class Foo
{
public static implicit operator int ();
}
API po zmianie:
public class Foo
{
public static implicit operator int ();
public static implicit operator float ();
}
Przykładowy kod klienta działający przed zmianą i uszkodzony po niej:
void Bar(int x);
void Bar(float x);
Bar(new Foo());
Uwagi: F # nie jest zepsuty, ponieważ nie ma obsługi przeciążonych operatorów na poziomie języka, ani jawnych, ani ukrytych-oba muszą być wywoływane bezpośrednio jako metody op_Explicit
i op_Implicit
.
Dodawanie nowych metod instancji
Kind: Source-level quiet semantyka się zmienia.
Języki, których dotyczy problem: C#, VB
Języki bez wpływu: F #
API przed zmianą:
public class Foo
{
}
API po zmianie:
public class Foo
{
public void Bar();
}
Przykładowy kod klienta, który ulega cichej zmianie semantycznej:
public static class FooExtensions
{
public void Bar(this Foo foo);
}
new Foo().Bar();
Uwagi: F # nie jest zepsuty, ponieważ nie ma obsługi poziomu językowego dla ExtensionMethodAttribute
i wymaga, aby metody rozszerzenia CLS były wywoływane jako metody statyczne.
13 answers
Zmiana podpisu metody
Kind: Binary-level Break
Języki, których dotyczy problem: C #(VB i F# najprawdopodobniej, ale niesprawdzone)
API przed zmianą
public static class Foo
{
public static void bar(int i);
}
API po zmianie
public static class Foo
{
public static bool bar(int i);
}
Przykładowy kod klienta działający przed zmianą
Foo.bar(13);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-24 17:00:45
Dodanie parametru z wartością domyślną.
Rodzaj przerwy: Binary-level break
Nawet jeśli wywołujący kod źródłowy nie musi się zmieniać, nadal musi zostać przekompilowany (tak jak przy dodawaniu zwykłego parametru).
Dzieje się tak dlatego, że C# kompiluje domyślne wartości parametrów bezpośrednio do zestawu wywołującego. Oznacza to, że jeśli nie przekompilujesz, otrzymasz wyjątek MissingMethodException, ponieważ stary assembly próbuje wywołać metodę z mniejszą argumenty.
API przed zmianą
public void Foo(int a) { }
API po zmianie
public void Foo(int a, string b = null) { }
Przykładowy kod klienta, który jest później łamany
Foo(5);
Kod klienta musi zostać przekompilowany do Foo(5, null)
na poziomie kodu bajtowego. Wywołane zgromadzenie będzie zawierać tylko Foo(int, string)
, a nie Foo(int)
. Dzieje się tak, ponieważ domyślne wartości parametrów są czysto językową funkcją, środowisko uruchomieniowe.Net nic o nich nie wie. (To również wyjaśnia, dlaczego wartości domyślne muszą być stałe czasu kompilacji w C#).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-07 12:13:02
Ten był bardzo nieoczywisty, kiedy go odkryłem, zwłaszcza w świetle różnicy w tej samej sytuacji dla interfejsów. To wcale nie jest przerwa, ale na tyle zaskakujące, że zdecydowałem się ją dołączyć: {]}
Refaktoryzacja członków klasy do klasy bazowej
Kind: not a break!
Języki, których dotyczy problem: brak (tzn. żaden nie jest uszkodzony)
API przed zmianą:
class Foo
{
public virtual void Bar() {}
public virtual void Baz() {}
}
API po zmianie:
class FooBase
{
public virtual void Bar() {}
}
class Foo : FooBase
{
public virtual void Baz() {}
}
Przykładowy kod, który działa na całym zmiana (choć spodziewałem się, że się zepsuje):
// C++/CLI
ref class Derived : Foo
{
public virtual void Baz() {{
// Explicit override
public virtual void BarOverride() = Foo::Bar {}
};
Uwagi:
C++ / CLI jest jedynym językiem. NET, który ma konstrukcję analogiczną do implementacji explicit interface dla wirtualnych członków klasy bazowej - "explicit override". W pełni spodziewałem się, że spowoduje to taki sam rodzaj złamania jak przy przenoszeniu elementów interfejsu do interfejsu bazowego (ponieważ IL generowane dla jawnego nadpisywania jest taki sam jak dla jawnej implementacji). Ku mojemu zdziwieniu tak nie jest - mimo że generowane IL nadal precyzuje, że BarOverride
nadpisuje Foo::Bar
zamiast FooBase::Bar
, assembly loader jest wystarczająco inteligentny, aby poprawnie zastąpić jedną za drugą, bez żadnych skarg - najwyraźniej fakt, że Foo
jest klasą, jest tym, co robi różnicę. Pomyśl...
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-05 22:24:58
Ten jest być może nie tak oczywistym szczególnym przypadkiem "dodawania / usuwania członków interfejsu" i uznałem, że zasługuje na swój własny wpis w świetle innego przypadku, który zamierzam opublikować dalej. Więc:
Refaktoryzacja elementów interfejsu do interfejsu bazowego
Rodzaj: przerwy na poziomie źródłowym i binarnym
Języki, których dotyczy problem: C#, VB, C++/ CLI, F# (dla przerwania źródła; binarny w naturalny sposób wpływa na każdy język)
API przed zmianą:
interface IFoo
{
void Bar();
void Baz();
}
API po zmiana:
interface IFooBase
{
void Bar();
}
interface IFoo : IFooBase
{
void Baz();
}
Przykładowy kod klienta, który jest łamany przez zmianę na poziomie źródła:
class Foo : IFoo
{
void IFoo.Bar() { ... }
void IFoo.Baz() { ... }
}
Przykładowy kod klienta, który jest łamany przez zmianę na poziomie binarnym;
(new Foo()).Bar();
Uwagi:
Problem polega na tym, że C#, VB i C++ / CLI wymagają dokładnej nazwy interfejsu w deklaracji implementacji członka interfejsu; zatem, jeśli członek zostanie przeniesiony do interfejsu bazowego, kod nie będzie już kompilowany.
Przerwa binarna wynika z faktu, że metody interfejsu są w pełni kwalifikowane w generowanym IL dla jawnych implementacji, a nazwa interfejsu również musi być dokładna.
Implementacja ukryta tam, gdzie jest dostępna (tj. C# i C++ / CLI, ale nie VB) będzie działać dobrze zarówno na poziomie źródłowym, jak i binarnym. Wywołania metod również nie pękają.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-13 19:42:54
Zmiana kolejności wyliczonych wartości
Rodzaj przerwy: Zmiana semantyki na poziomie źródłowym/binarnym
Języki, których dotyczy problem: wszystkie
Zmiana kolejności wyliczonych wartości zachowa kompatybilność na poziomie źródłowym, ponieważ literały mają tę samą nazwę, ale ich indeksy porządkowe zostaną zaktualizowane, co może powodować pewne rodzaje cichych przerw na poziomie źródłowym.
Jeszcze gorzej jest z cichymi przerwami na poziomie binarnym, które można wprowadzić, jeśli kod klienta nie zostanie przekompilowany w nowym API wersja. Wartości Enum są stałymi czasu kompilacji i jako takie wszelkie ich zastosowania są wypiekane do IL klienta assembly. Ta sprawa może być czasami szczególnie trudna do wykrycia.
API przed zmianą
public enum Foo
{
Bar,
Baz
}
API po zmianie
public enum Foo
{
Baz,
Bar
}
Przykładowy kod klienta, który działa, ale jest później zepsuty:
Foo.Bar < Foo.Baz
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-04 20:47:52
To jest naprawdę bardzo rzadka rzecz w praktyce, ale mimo to zaskakująca, gdy to się dzieje.
Dodawanie nowych Nie przeciążonych członków
Kind: Source level break lub quiet semantics change.
Języki, których dotyczy problem: C#, VB
Nie dotyczy to języków: F#, C++ / CLIAPI przed zmianą:
public class Foo
{
}
API po zmianie:
public class Foo
{
public void Frob() {}
}
Przykładowy kod klienta, który jest łamany przez zmianę:
class Bar
{
public void Frob() {}
}
class Program
{
static void Qux(Action<Foo> a)
{
}
static void Qux(Action<Bar> a)
{
}
static void Main()
{
Qux(x => x.Frob());
}
}
Uwagi:
Problem tutaj jest spowodowany przez wnioskowanie typu lambda w C # i VB w obecności rozdzielczości przeciążenia. Ograniczona forma typowania kaczek jest tutaj stosowana do zerwania więzi, w których pasuje więcej niż jeden typ, sprawdzając, czy ciało lambda ma sens dla danego typu - jeśli tylko jeden typ daje kompilowalne ciało, to ten jest wybrany.
Niebezpieczeństwo polega na tym, że kod klienta może mieć przeciążoną grupę metod, gdzie niektóre metody przyjmują argumenty jego własnych typów, a inne przyjmują argumenty typów narażonych przez twoje biblioteka. Jeśli którykolwiek z jego kodu opiera się na algorytmie wnioskowania typu, aby określić poprawną metodę opartą wyłącznie na obecności lub braku członków, dodanie nowego członka do jednego z typów o tej samej nazwie, co w jednym z typów klienta, może potencjalnie odrzucić wnioskowanie, powodując niejednoznaczność podczas rozwiązywania przeciążeń.
Zauważ, że typy Foo
i Bar
w tym przykładzie nie są powiązane w żaden sposób, ani przez dziedziczenie, ani w inny sposób. Samo ich użycie w jednej grupie metod jest wystarczy, aby to uruchomić, a jeśli to nastąpi w kodzie klienta, nie masz nad tym kontroli.
Powyższy przykładowy kod pokazuje prostszą sytuację, w której jest to przerwa na poziomie źródłowym (np. wynik błędu kompilatora). Jednak może to być również cicha zmiana semantyki, jeśli przeciążenie wybrane przez wnioskowanie miało inne argumenty, które w przeciwnym razie spowodowałyby jego klasyfikację poniżej (np. opcjonalne argumenty z wartościami domyślnymi lub niedopasowanie typu między zadeklarowanym i faktycznym argumentem wymagającym konwersja ukryta). W takim scenariuszu rozdzielczość przeciążenia nie będzie już zawieść, ale inne przeciążenie będzie cicho wybrane przez kompilator. W praktyce jednak bardzo trudno jest znaleźć się w tej sprawie bez starannego konstruowania podpisów metod, aby celowo ją wywołać.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-21 23:14:19
Konwertuje implementację interfejsu implicit na jawną.
Rodzaj przerwy: źródło i binarny
Języki, Których Dotyczy Problem: Wszystkie
Jest to tak naprawdę tylko odmiana zmiany dostępności metody - jest to tylko trochę bardziej subtelne, ponieważ łatwo przeoczyć fakt, że nie każdy dostęp do metod interfejsu musi odbywać się poprzez odniesienie do typu interfejsu.
API przed zmianą:
public class Foo : IEnumerable
{
public IEnumerator GetEnumerator();
}
API po Zmiana:
public class Foo : IEnumerable
{
IEnumerator IEnumerable.GetEnumerator();
}
Przykładowy kod klienta, który działa przed zmianą i jest później zepsuty:
new Foo().GetEnumerator(); // fails because GetEnumerator() is no longer public
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-27 13:36:59
Konwertuje jawną implementację interfejsu do niejawnej.
Rodzaj przerwy: Źródło
Języki, Których Dotyczy Problem: Wszystkie
Refaktoryzacja jawnej implementacji interfejsu do niejawnej jest bardziej subtelna w tym, jak może złamać API. Z pozoru wydaje się, że powinno to być stosunkowo bezpieczne, jednak w połączeniu z dziedziczeniem może powodować problemy.
API przed zmianą:
public class Foo : IEnumerable
{
IEnumerator IEnumerable.GetEnumerator() { yield return "Foo"; }
}
API po zmianie:
public class Foo : IEnumerable
{
public IEnumerator GetEnumerator() { yield return "Foo"; }
}
Próbka Kod klienta, który działa przed zmianą i jest później zepsuty:
class Bar : Foo, IEnumerable
{
IEnumerator IEnumerable.GetEnumerator() // silently hides base instance
{ yield return "Bar"; }
}
foreach( var x in new Bar() )
Console.WriteLine(x); // originally output "Bar", now outputs "Foo"
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-27 13:36:54
Zmiana pola na właściwość
Rodzaj przerwy: API
Języki, których dotyczy problem: Visual Basic i C#*
Info: gdy zmienisz zwykłe pole lub zmienną na właściwość w visual basic, każdy Zewnętrzny kod odwołujący się do tego elementu będzie musiał zostać przekompilowany.
API przed zmianą:
Public Class Foo
Public Shared Bar As String = ""
End Class
API po zmianie:
Public Class Foo
Private Shared _Bar As String = ""
Public Shared Property Bar As String
Get
Return _Bar
End Get
Set(value As String)
_Bar = value
End Set
End Property
End Class
Przykładowy kod klienta, który działa, ale jest później zepsuty:
Foo.Bar = "foobar"
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-29 14:25:56
Dodawanie Przestrzeni Nazw
Źródło-level break / źródło-level quiet semantics change
Ze względu na sposób rozdzielania przestrzeni nazw w vb.Net dodanie przestrzeni nazw do biblioteki może spowodować, że kod Visual Basic skompilowany z poprzednią wersją API nie będzie kompilowany z nową wersją.
Przykładowy kod klienta:
Imports System
Imports Api.SomeNamespace
Public Class Foo
Public Sub Bar()
Dim dr As Data.DataRow
End Sub
End Class
Jeśli nowa wersja API doda przestrzeń nazw Api.SomeNamespace.Data
, powyższy kod nie zostanie skompilowany.
Staje się bardziej skomplikowane z importem przestrzeni nazw na poziomie projektu. Jeśli Imports System
jest pominięty w powyższym kodzie, ale przestrzeń nazw System
jest importowana na poziomie projektu, kod może nadal powodować błąd.
Jednakże, jeśli Api zawiera klasę DataRow
w swojej przestrzeni nazw Api.SomeNamespace.Data
, to kod zostanie skompilowany, ale {[15] } będzie instancją System.Data.DataRow
W przypadku kompilacji ze starą wersją API i Api.SomeNamespace.Data.DataRow
W przypadku kompilacji z nową wersją API.
Argument Zmiana nazwy
Źródło-poziom break
Zmiana nazw argumentów jest przełomową zmianą w vb.net od wersji 7 (?) (. Net Wersja 1?) i c#.net od wersji 4 (. Net version 4).
API przed zmianą:
namespace SomeNamespace {
public class Foo {
public static void Bar(string x) {
...
}
}
}
API po zmianie:
namespace SomeNamespace {
public class Foo {
public static void Bar(string y) {
...
}
}
}
Przykładowy kod klienta:
Api.SomeNamespace.Foo.Bar(x:"hi"); //C#
Api.SomeNamespace.Foo.Bar(x:="hi") 'VB
Parametry Ref
Źródło-poziom break
Dodanie nadpisania metody z tym samym podpisem, z wyjątkiem tego, że jeden parametr jest przekazywany przez reference zamiast by spowoduje, że źródło vb, które odwołuje się do API, nie będzie w stanie rozwiązać funkcji. Visual Basic nie ma możliwości(?), aby odróżnić te metody w punkcie wywoławczym, chyba że mają różne nazwy argumentów, więc taka zmiana może spowodować, że oba elementy będą bezużyteczne z kodu vb.
API przed zmianą:
namespace SomeNamespace {
public class Foo {
public static void Bar(string x) {
...
}
}
}
API po zmianie:
namespace SomeNamespace {
public class Foo {
public static void Bar(string x) {
...
}
public static void Bar(ref string x) {
...
}
}
}
Przykładowy kod klienta:
Api.SomeNamespace.Foo.Bar(str)
Pole do zmiany właściwości
Poziom binarny break / Source - level break
Oprócz oczywistego podziału na poziomie binarnym, może to spowodować podział na poziomie źródłowym, jeśli element zostanie przekazany do metody przez odniesienie.
API przed zmianą:
namespace SomeNamespace {
public class Foo {
public int Bar;
}
}
API po zmianie:
namespace SomeNamespace {
public class Foo {
public int Bar { get; set; }
}
}
Przykładowy kod klienta:
FooBar(ref Api.SomeNamespace.Foo.Bar);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-11 16:20:45
Zmiana API:
- dodanie atrybutu [przestarzałe] (trochę przykryłeś to wymienianiem atrybutów; jednak może to być przełomowa Zmiana podczas używania warning-as-error.)
Binary-level break:
- przenoszenie typu z jednego zestawu do drugiego
- Zmiana przestrzeni nazw typu
- dodawanie typu klasy bazowej z innego zestawu.
-
Dodawanie nowego elementu (event protected), który używa typu z innego zestawu (Class2) jako ograniczenie argumentu szablonu.
protected void Something<T>() where T : Class2 { }
-
Zmiana klasy potomnej (Class3) na derived from a type in another assembly, gdy klasa jest używana jako argument szablonu dla tej klasy.
protected class Class3 : Class2 { } protected void Something<T>() where T : Class3 { }
Zmiana semantyki na poziomie źródłowym:
- Dodawanie/usuwanie / zmiana przesłaniań Equals(), GetHashCode () lub ToString ()
(Nie wiem gdzie te pasują)
Wdrożenie zmiany:
- Dodawanie / usuwanie zależności / odniesień
- Aktualizacja zależności do nowszych wersji [7]} Zmiana 'platformy docelowej' pomiędzy x86, Itanium, x64 lub anycpu Instalacja 3.5 na skrzynce. Net 2.0 pozwala na wywołania API, które następnie wymagają. Net 2.0 SP2)
Bootstrap / zmiany konfiguracji:
- Dodawanie/usuwanie / zmiana niestandardowych opcji konfiguracyjnych (np.config Ustawienia)
- przy intensywnym użyciu IoC / DI W dzisiejszych aplikacjach, konieczne jest ponowne skonfigurowanie i / lub zmiana kodu startowego dla kodu zależnego od DI.
Aktualizacja:
Przepraszam, nie zdawałem sobie sprawy, że jedynym powodem, dla którego to było dla mnie łamanie było to, że używałem ich w ograniczeniach szablonów.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-30 20:38:17
Dodawanie metod przeciążania w celu usunięcia domyślnych parametrów użycia
Rodzaj przerwy: Zmiana semantyki na poziomie źródłowym
Ponieważ kompilator przekształca wywołania metody z brakującymi wartościami domyślnych parametrów na jawne wywołanie z domyślną wartością po stronie wywołania, podana jest kompatybilność dla istniejącego skompilowanego kodu; metoda z poprawnym podpisem zostanie znaleziona dla wszystkich wcześniej skompilowanych kodów.
Po drugiej stronie wywołania bez użycia opcjonalnego parametry są teraz kompilowane jako wywołanie nowej metody, której brakuje opcjonalnego parametru. Wszystko nadal działa poprawnie, ale jeśli wywołany kod znajduje się w innym złożeniu, nowo skompilowany kod wywołujący go jest teraz zależny od nowej wersji tego złożenia. Deploying assemblies calling the refactored code without also deploying the assembly, w którym znajduje się refactored code, skutkuje wyjątkami "method not found".
API przed zmianą
public int MyMethod(int mandatoryParameter, int optionalParameter = 0)
{
return mandatoryParameter + optionalParameter;
}
API po Zmień
public int MyMethod(int mandatoryParameter, int optionalParameter)
{
return mandatoryParameter + optionalParameter;
}
public int MyMethod(int mandatoryParameter)
{
return MyMethod(mandatoryParameter, 0);
}
Przykładowy kod, który nadal będzie działał
public int CodeNotDependentToNewVersion()
{
return MyMethod(5, 6);
}
Przykładowy kod, który jest teraz zależny od nowej wersji podczas kompilacji
public int CodeDependentToNewVersion()
{
return MyMethod(5);
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-16 09:37:13
Zmiana nazwy interfejsu
Kind of Break: Source and Binary
Języki, których dotyczy problem: najprawdopodobniej wszystkie, Przetestowane w C#.
API przed zmianą:
public interface IFoo
{
void Test();
}
public class Bar
{
IFoo GetFoo() { return new Foo(); }
}
API po zmianie:
public interface IFooNew // Of the exact same definition as the (old) IFoo
{
void Test();
}
public class Bar
{
IFooNew GetFoo() { return new Foo(); }
}
Przykładowy kod klienta, który działa, ale jest później zepsuty:
new Bar().GetFoo().Test(); // Binary only break
IFoo foo = new Bar().GetFoo(); // Source and binary break
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-19 14:46:59