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.

Author: Pavel Minaev, 2009-09-22

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);
 38
Author: Justin Drury,
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#).

 34
Author: Eldritch Conundrum,
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...

 23
Author: Pavel Minaev,
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ą.

 17
Author: Pavel Minaev,
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
 12
Author: glopes,
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++ / CLI

API 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ć.

 10
Author: Pavel Minaev,
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
 7
Author: LBushkin,
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"
 5
Author: LBushkin,
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"
 5
Author: Hagelt18,
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);
 4
Author: jswolf19,
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:

  1. 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:

  1. przenoszenie typu z jednego zestawu do drugiego
  2. Zmiana przestrzeni nazw typu
  3. dodawanie typu klasy bazowej z innego zestawu.
  4. 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 { }
    
  5. 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:

  1. Dodawanie/usuwanie / zmiana przesłaniań Equals(), GetHashCode () lub ToString ()

(Nie wiem gdzie te pasują)

Wdrożenie zmiany:

  1. Dodawanie / usuwanie zależności / odniesień
  2. Aktualizacja zależności do nowszych wersji
  3. [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:

  1. Dodawanie/usuwanie / zmiana niestandardowych opcji konfiguracyjnych (np.config Ustawienia)
  2. 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.

 3
Author: csharptest.net,
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); 
  }
 2
Author: Udontknow,
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
 0
Author: Aidiakapi,
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