Frage

Ich möchte so viele Informationen wie möglich über API Versionsverwaltung in .NET / CLR, sammeln und zu speziell, wie API-Änderungen machen oder brechen nicht Client-Anwendungen. Lassen Sie uns zunächst einige Begriffe definieren:

API ändern - eine Änderung der öffentlich sichtbare Definition eines Typs, einschließlich seiner öffentlichen Mitglieder. Dazu gehören Namen Art und Änderungselement, Basistyp eines Typs Ändern, Hinzufügen / Schnittstellen aus der Liste der implementierten Schnittstellen eines Typs, Entfernen, Hinzufügen / Entfernen von Mitgliedern (einschließlich Überlast), Leitteil Sichtbarkeit, Umbenennen Verfahren und Typparametern, Hinzufügen Standardwerte für Methodenparameter, Hinzufügen / Entfernen von Attributen auf Typen und Member, und das Hinzufügen / generische Typparameter auf Typen und Entfernen von Mitgliedern (habe ich etwas verpasst?). Dies gilt nicht für alle Änderungen in Teilkörper oder Änderungen an private Mitglieder (das heißt wir berücksichtigen nicht die Spiegelung).

Binary-Level-Pause - eine API-Änderung, die möglicherweise nicht mit der neuen Version Laden kompilierten gegen ältere Version der API in Client-Baugruppen führt. Beispiel: Ändern Methodensignatur, auch wenn es in der gleichen Art und Weise wie zuvor (dh: void zurückzuTyp / Parameter Standardwerte Überlastungen) aufgerufen werden kann.

Source-Level-Pause - eine API-Änderung, die in vorhandenen Code führt geschrieben gegen ältere Version der API zu kompilieren möglicherweise nicht mit der neuen Version zu kompilieren. Bereits kompilierte Client-Baugruppen wie vor arbeiten, jedoch. Beispiel:. Hinzufügen eine neue Überladung, die in Mehrdeutigkeit in Methodenaufrufen, die waren eindeutig vorherige führen

Source-Level ruhig Semantik ändern - eine API-Änderung, die in vorhandenen Code führt geschrieben gegen ältere Version der API leise seine Semantik ändern zu kompilieren, z.B. durch eine andere Methode aufrufen. Der Code sollte jedoch weiterhin ohne Warnungen / Fehler zu kompilieren, und zuvor kompilierten Baugruppen sollten nach wie vor arbeiten. Beispiel:. Eine neue Schnittstelle auf einer bestehenden Klasse implementieren, die in einer anderen Überlastung führt bei Überlast Auflösung gewählt wird,

Das ultimative Ziel ist es, so viele Bruch und ruhig Semantik API-Änderungen wie möglich zu katalogisieren und genaue Wirkung von Bruch zu beschreiben, und welche Sprachen und sind nicht davon betroffen. Um auf diesem zu erweitern: während einige Änderungen alle Sprachen betreffen allgemein (beispielsweise ein neues Mitglied zu einer Schnittstelle hinzugefügt brechen Implementierungen dieser Schnittstelle in einem beliebigen Sprache), einige erfordern sehr spezifische Sprache Semantik ins Spiel eingeben, um eine Pause. Dies umfasst in der Regel die meisten Verfahren Überlastung, und in der Regel nichts mit impliziten Typkonvertierungen zu tun. Es scheint keinen Weg, um den „kleinsten gemeinsamen Nenner“ hier auch für CLS-konforme Sprachen zu definieren (dh solche, die mindestens an Regeln der „CLS Verbraucher“ entsprechen, wie in CLI-Spezifikation definiert) - obwohl ich zu schätzen wissen, wenn jemand korrigiert mich als falsch, hier zu sein - so wird dies durch die Sprache der Sprache gehen. Diejenigen, von größtem Interesse sind natürlich diejenigen, die mit .NET kommen aus der Box: C #, VB und F #; aber andere, wie Ironpython, IronRuby, Delphi Prism usw. sind auch relevant. Je mehr eine Ecke Fall ist, desto interessanter wird es sein - Dinge wie Mitglieder zu entfernen sind ziemlich selbstverständlich, aber subtile Wechselwirkungen zwischen z.B. Verfahren Überlastung, optional / Standardparameter, Lambda Typinferenz und Konvertierungsoperator können manchmal sehr überraschend sein.

Einige Beispiele Kickstart folgt aus:

Hinzufügen von neuer Methode Überlastungen

Typ: Source-Level-Pause

Sprachen betroffen: C #, VB, F #

API vor der Änderung:

public class Foo
{
    public void Bar(IEnumerable x);
}

API nach der Änderung:

public class Foo
{
    public void Bar(IEnumerable x);
    public void Bar(ICloneable x);
}

Beispiel Client-Code vor der Änderung zu arbeiten und gebrochen, nachdem es:

new Foo().Bar(new int[0]);

Das Hinzufügen von neuen impliziten Konvertierungsoperator Überlastungen

Art. Source-Level-Pause

Sprachen einffected: C #, VB

Sprachen nicht betroffen: F #

API vor der Änderung:

public class Foo
{
    public static implicit operator int ();
}

API nach der Änderung:

public class Foo
{
    public static implicit operator int ();
    public static implicit operator float ();
}

Beispiel Client-Code vor der Änderung zu arbeiten und gebrochen, nachdem es:

void Bar(int x);
void Bar(float x);
Bar(new Foo());

Weitere Informationen: F # ist nicht gebrochen, weil es keine Sprache-Level-Support für überladene Operatoren hat, weder explizit noch implizit - sowohl direkt als op_Explicit und op_Implicit Methoden aufgerufen werden müssen

.

Das Hinzufügen von neuen Instanz-Methoden

Art. Source-Level ruhig Semantik ändern

Sprachen betroffen: C #, VB

Sprachen nicht betroffen: F #

API vor der Änderung:

public class Foo
{
}

API nach der Änderung:

public class Foo
{
    public void Bar();
}

Beispiel Client-Code, der eine ruhige Semantik Änderung leidet:

public static class FooExtensions
{
    public void Bar(this Foo foo);
}

new Foo().Bar();

Weitere Informationen: F # ist nicht gebrochen, weil sie nicht die Sprache Level-Support für ExtensionMethodAttribute haben und erfordert Methoden CLS Erweiterung als statische Methoden aufgerufen werden.

War es hilfreich?

Lösung

Ändern einer Methodensignatur

Typ: Binär-Ebene Pause

Sprachen betroffen: C # (VB und F # höchstwahrscheinlich, aber nicht getestet)

API vor der Änderung

public static class Foo
{
    public static void bar(int i);
}

API nach der Änderung

public static class Foo
{
    public static bool bar(int i);
}

Beispiel Client-Code vor der Änderung arbeitet

Foo.bar(13);

Andere Tipps

einen Parameter mit einem Standardwert hinzufügen.

Art Break: Binary-Ebene Pause

Auch wenn der Angerufene Quellcode nicht ändern müssen, es muss noch neu kompiliert werden (wie wenn ein regelmäßigen Parameter hinzuzufügen).

Das ist, weil C # die Standardwerte der Parameter direkt in die aufrufende Assembly kompiliert. Es bedeutet, dass, wenn Sie nicht neu kompilieren, können Sie eine Missing bekommen, weil die alte Baugruppe ein Verfahren mit weniger Argumenten zu nennen versucht.

API Vor Ändern

public void Foo(int a) { }

API Nach Ändern

public void Foo(int a, string b = null) { }

Beispiel-Client-Code, der danach gebrochen

Foo(5);

Der Client-Code muss in Foo(5, null) auf der Bytecode-Ebene neu kompiliert werden. Die genannte Baugruppe enthält nur Foo(int, string), nicht Foo(int). Das ist, weil Standard-Parameterwerte eine reine Sprache-Funktion sind, die .NET-Laufzeit weiß nichts über sie. (Dies auch erklären, warum die Standardwerte seinen Kompilierung-Konstanten in C #).

Dies war sehr nicht offensichtlich, wenn ich es entdecken, vor allem in Anbetracht der Differenz mit der gleichen Situation für Schnittstellen. Es ist nicht eine Pause überhaupt, aber es ist überraschend genug, dass ich es schließen beschlossen:

Refactoring Klasse in eine Basisklasse

Art: kein Bruch

Sprachen betroffen: keine (das heißt keine gebrochen)

API vor der Änderung:

class Foo
{
    public virtual void Bar() {}
    public virtual void Baz() {}
}

API nach der Änderung:

class FooBase
{
    public virtual void Bar() {}
}

class Foo : FooBase
{
    public virtual void Baz() {}
}

Beispielcode, der in der gesamten Veränderung arbeitet weiter (obwohl ich es zu brechen erwartet):

// C++/CLI
ref class Derived : Foo
{
   public virtual void Baz() {{

   // Explicit override    
   public virtual void BarOverride() = Foo::Bar {}
};

Weitere Informationen:

C ++ / CLI ist die einzige .NET-Sprache, die ein Konstrukt analog explizite Schnittstellenimplementierung für virtuelle Basisklassen Mitglieder hat - „explizit überschreiben“. I erwartet, dass vollständig in der gleichen Art von Bruch zu führen, als wenn Schnittstellenelement an eine Basisoberfläche zu bewegen (da IL für explizite Überschreibung erzeugte die gleiche wie für explizite Implementierung ist). Zu meiner Überraschung ist dies nicht der Fall - obwohl erzeugen IL gibt immer noch, dass BarOverride Überschreibungen Foo::Bar statt FooBase::Bar, Montag Lader intelligent genug ist, eine für eine andere ohne Beanstandungen richtig zu ersetzen - offenbar die Tatsache, dass Foo eine Klasse ist, was macht den Unterschied. Go figure ...

Dieses ist ein vielleicht nicht so offensichtliche Sonderfall „Hinzufügen / Entfernen von Interface-Mitglieder“, und ich dachte, es in Licht einer anderen Fall einen eigenen Eintrag verdient, die ich neben veröffentlichen werde. Also:

Refactoring-Schnittstelle Mitglieder in eine Basis-Schnittstelle

Typ: Brüche an beiden Quell- und binären Ebenen

Sprachen betroffen: C #, VB, C ++ / CLI, F # (für Quelle break; binäre Eins wirkt sich natürlich jede Sprache)

API vor der Änderung:

interface IFoo
{
    void Bar();
    void Baz();
}

API nach der Änderung:

interface IFooBase 
{
    void Bar();
}

interface IFoo : IFooBase
{
    void Baz();
}

Beispiel-Client-Code, der durch eine Änderung an der Quelle Ebene gebrochen ist:

class Foo : IFoo
{
   void IFoo.Bar() { ... }
   void IFoo.Baz() { ... }
}

Beispiel-Client-Code, der durch die Änderung in binärer Ebene gebrochen ist;

(new Foo()).Bar();

Weitere Informationen:

Für Source-Level-Break, das Problem ist, dass C #, VB und C ++ / CLI erfordern all genau Schnittstellennamen in der Deklaration von Interface-Member-Implementierung; so, wenn das Teil einer Basisschnittstelle bewegt wird, wird der Code nicht mehr kompilieren.

Binary Pause ist aufgrund der Tatsache, dass Interface-Methoden für explizite Implementierungen in generierten IL voll qualifiziert sind, und Schnittstellennamen muss auch genau sein.

Implizite Implementierung, bei der zur Verfügung (das heißt C # und C ++ / CLI, aber nicht VB) werden sowohl auf Quell- und binärer Ebene funktionieren. Methodenaufrufe entweder nicht brechen.

Neuordnen aufgezählten Werte

Art der Pause: Source-Level / Binary-Ebene ruhig Semantik ändern

Sprachen betroffen: alle

Die Neuanordnung aufgezählten Werte halten Source-Level-Kompatibilität als Literale den gleichen Namen haben, aber ihre Ordnungsindizes aktualisiert werden, die einige Arten von stillen Source-Level-Brüche verursachen können.

Noch schlimmer sind die stillen binary-Ebene Pausen, die eingeführt werden können, wenn Client-Code gegen die neue API-Version nicht neu kompiliert wird. Enum-Werte sind Kompilierung-Konstanten und als solche alle Verwendungen von ihnen gebacken werden in die Client-Assembly IL. Dieser Fall kann besonders schwierig sein, manchmal zu erkennen.

API Vor Ändern

public enum Foo
{
   Bar,
   Baz
}

API Nach Ändern

public enum Foo
{
   Baz,
   Bar
}

Beispiel Client-Code, das funktioniert aber gebrochen danach:

Foo.Bar < Foo.Baz

Dies ist wirklich eine sehr seltene Sache in der Praxis, aber dennoch ein überraschend ein, wenn es passiert.

Hinzufügen neue nicht-überladenen Mitglieder

Art. Source-Level-Pause oder ruhig Semantik ändern

Sprachen betroffen: C #, VB

Sprachen nicht betroffen: F #, C ++ / CLI

API vor der Änderung:

public class Foo
{
}

API nach der Änderung:

public class Foo
{
    public void Frob() {}
}

Beispiel Client-Code, der durch Änderung gebrochen wird:

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());        
    }
}

Weitere Informationen:

Das Problem wird durch Lambda Typinferenz in C # und VB in Anwesenheit von Überladungsauflösung verursacht. Eine begrenzte Form von Duck Typing ist hier beschäftigt Bindungen zu brechen, wo mehr als eine Art übereinstimmt, indem geprüft wird, ob der Körper des Lambda-Sinn für einen bestimmten Typ macht -. Wenn nur ein Typ Ergebnisse in übersetzbar Körper, dass man gewählt wird,

Die Gefahr dabei ist, dass Client-Code eine überladene Methode Gruppe haben kann, wo einige Methoden Argumente seiner eigenen Typen nehmen, und andere nehmen Argumente der Typen von Ihrer Bibliothek ausgesetzt. Wenn jeder seinen Code stützt sich dann auf Typinferenz Algorithmus die richtige Methode, um zu bestimmen beruht allein auf das Vorhandensein oder Fehlen von Mitgliedern, dann ein neues Mitglied das Hinzufügen zu einem Ihrer Typen mit dem gleichen Namen wie in einem der Typen des Kunden kann möglicherweise Inferenz werfen ab, während der Überladungsauflösung in Mehrdeutigkeit zur Folge hat.

Beachten Sie, dass Typen Foo und Bar in diesem Beispiel nicht in irgendeiner Weise verbunden sind, nicht durch Vererbung noch aus anderen Gründen. Die bloße Nutzung von ihnen in einer einzigen Methode Gruppe ist genug, um dies zu triggern, und, wenn dies im Client-Code auftritt, haben Sie keine Kontrolle über sie.

Der obige Beispielcode zeigt eine einfachere Situation, wo dies eine Source-Level-Pause ist (das heißt Compiler-Fehler). Dies kann jedoch auch eine stille Semantik ändern, wenn die Überlast, die über Inferenz gewählt andere Argumente hat, die sonst dazu führen, wäre es mit Standardwerten oder Typenkonflikt zwischen deklarierte und tatsächliches Argument erfordert eine impliziten Platz unterhalb (zB optionalen Argumente wird Umwandlung). In einem solchen Szenario wird die Überladungsauflösung nicht mehr fehl, aber eine andere Überlastung wird ruhig durch den Compiler ausgewählt werden. In der Praxis ist es jedoch sehr schwer, in diesen Fall auszuführen, ohne Methodensignaturen sorgfältig Konstruktion absichtlich es zu verursachen.

Konvertieren eine implizite Schnittstellen-Implementierung in eine expliziten ein.

Art der Pause: Source und Binary

Sprachen Betroffene: Alle

Das ist wirklich nur eine Variante eines Verfahrens der Zugänglichkeit zu ändern -. Es ist nur ein wenig subtile, da es einfach ist, die Tatsache zu übersehen, dass nicht alle Zugriff auf das Verfahren der Schnittstelle notwendigerweise durch einen Hinweis auf die Art der Schnittstelle ist

API Vor Änderung:

public class Foo : IEnumerable
{
    public IEnumerator GetEnumerator();
}

API Nach Wechsel:

public class Foo : IEnumerable
{
    IEnumerator IEnumerable.GetEnumerator();
}

Beispiel-Client-Code, der vor der Änderung arbeitet und gebrochen danach:

new Foo().GetEnumerator(); // fails because GetEnumerator() is no longer public

Konvertieren eine explizite Interface-Implementierung in einer impliziten ein.

Art Break: Quelle

Sprachen Betroffene: Alle

Der Refactoring einer expliziten Interface-Implementierung in eine implizites ist subtiler, wie es eine API brechen kann. An der Oberfläche scheint es, dass diese relativ sicher sein sollten, aber wenn sie mit Vererbung kombiniert wird, kann es zu Problemen führen.

API Vor Änderung:

public class Foo : IEnumerable
{
    IEnumerator IEnumerable.GetEnumerator() { yield return "Foo"; }
}

API Nach Wechsel:

public class Foo : IEnumerable
{
    public IEnumerator GetEnumerator() { yield return "Foo"; }
}

Beispiel-Client-Code, der vor der Änderung arbeitet und gebrochen danach:

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"

Ändern eines Feldes auf eine Eigenschaft

Art Break: API

Sprachen Betroffene: Visual Basic und C # *

Info: Wenn Sie ein normales Feld oder eine Variable in eine Eigenschaft in Visual Basic ändern, jeder außerhalb Code, der Mitglied in irgendeiner Weise Referenzierung müssen neu kompiliert werden.

API Vor Änderung:

Public Class Foo    
    Public Shared Bar As String = ""    
End Class

API Nach Wechsel:

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    

Beispiel Client-Code, das funktioniert aber gebrochen danach:

Foo.Bar = "foobar"

Namespace Zusatz

Source-Level-Break / Quelle-Ebene ruhig Semantik ändern

Durch die Art und Weise Namespace Auflösung arbeitet in vb.Net, einen Namespace zu einer Bibliothek hinzufügen, kann Visual Basic-Code verursachen, die mit einer früheren Version der API kompilierte nicht mit einer neuen Version zu kompilieren.

Beispiel Client-Code:

Imports System
Imports Api.SomeNamespace

Public Class Foo
    Public Sub Bar()
        Dim dr As Data.DataRow
    End Sub
End Class

Wenn eine neue Version der API den Namespace Api.SomeNamespace.Data fügt hinzu, dann wird der obige Code wird nicht kompiliert.

Es wird komplizierter, mit Projektebene Namespace Importe. Wenn Imports System aus dem obigen Code nicht angegeben, aber die System Namespace wird auf Projektebene importiert, dann noch der Code kann zu einem Fehler führen.

Wenn jedoch die Api eine Klasse DataRow in seinem Api.SomeNamespace.Data Namespace enthält, dann wird der Code kompilieren, aber dr wird eine Instanz von System.Data.DataRow sein, wenn sie mit der alten Version der API und Api.SomeNamespace.Data.DataRow kompiliert, wenn mit der neuen Version der API kompiliert .

Argument umbenennen

Source-Level-Pause

Ändern der Namen der Argumente ist eine unterbrechende Änderung in vb.net ab Version 7 (?) (.Net Version 1?) Und c # .net ab Version 4 (.Net Version 4).

API vor der Änderung:

namespace SomeNamespace {
    public class Foo {
        public static void Bar(string x) {
           ...
        }
    }
}

API nach der Änderung:

namespace SomeNamespace {
    public class Foo {
        public static void Bar(string y) {
           ...
        }
    }
}

Beispiel Client-Code:

Api.SomeNamespace.Foo.Bar(x:"hi"); //C#
Api.SomeNamespace.Foo.Bar(x:="hi") 'VB

Ref Parameter

Source-Level-Pause

ein Verfahren Überschreibung mit derselben Signatur der Ausnahme, dass ein Parameter ist das Hinzufügen von Referenz übergeben, anstatt nach Wert wird vb Quelle verursachen, die die API-Referenzen zu treffen nicht in der Lage, die Funktion zu lösen. Visual Basic hat keine Möglichkeit, (?), Diese Methoden im Call-Punkt zu unterscheiden, wenn sie unterschiedliche Argument Namen haben, so eine solche Änderung beide Mitglieder dazu führen könnten, von vb Code unbrauchbar zu sein.

API vor der Änderung:

namespace SomeNamespace {
    public class Foo {
        public static void Bar(string x) {
           ...
        }
    }
}

API nach der Änderung:

namespace SomeNamespace {
    public class Foo {
        public static void Bar(string x) {
           ...
        }
        public static void Bar(ref string x) {
           ...
        }
    }
}

Beispiel Client-Code:

Api.SomeNamespace.Foo.Bar(str)

Feld auf Eigenschaften ändern

Binary-Level-Pause / Source-Level-Pause

Neben der offensichtlichen binary-Ebene Pause, das ist ein Source-Level-Bruch führen kann, wenn das Mitglied ein Verfahren durch Verweis übergeben wird.

API vor der Änderung:

namespace SomeNamespace {
    public class Foo {
        public int Bar;
    }
}

API nach der Änderung:

namespace SomeNamespace {
    public class Foo {
        public int Bar { get; set; }
    }
}

Beispiel Client-Code:

FooBar(ref Api.SomeNamespace.Foo.Bar);

API Änderung:

  1. das [Obsolete] Attribut hinzufügen (Sie irgendwie dieses mit erwähnen Attributen bedeckt, aber dies eine Bruch Änderung sein kann, wenn Warnung-as-Fehler verwenden.)

Binary-Level-Pause:

  1. einen Typ aus einer Baugruppe in einer anderen
  2. Ändern der Namespace eines Typs
  3. eine Basisklasse Art von einer anderen Baugruppe Hinzufügen.
  4. Das Hinzufügen ein neues Mitglied (Ereignis geschützt), die eine Art von einer anderen Baugruppe (Klasse 2) als Templat Argument Constraint verwendet.

    protected void Something<T>() where T : Class2 { }
    
  5. Ändern eine Kindklasse (Klasse 3) von einem Typ in einer anderen Baugruppe abzuleiten, wenn die Klasse als Vorlage Argument für diese Klasse verwendet wird.

    protected class Class3 : Class2 { }
    protected void Something<T>() where T : Class3 { }
    

Source-Level ruhig Semantik ändern:

  1. Hinzufügen / Entfernen / Ändern Überschreibungen von Equals (), GetHashCode () oder ToString ()

(nicht sicher, wo diese passen)

Deployment Änderungen:

  1. Hinzufügen / Entfernen von Abhängigkeiten / Referenzen
  2. Die Aktualisierung der Abhängigkeiten auf neuere Versionen
  3. Ändern der 'Zielplattform' zwischen x86, Itanium, x64 oder anycpu
  4. Bauen / Prüfung auf einem anderen Rahmen installieren (das heißt die Installation von 3.5 auf einer .NET 2.0-Box ermöglicht API-Aufrufe, die erfordern dann .Net 2.0 SP2)

Bootstrap / Konfigurationsänderungen:

  1. Hinzufügen / Entfernen / Ändern benutzerdefinierten Konfigurationsoptionen (das heißt App.config Einstellungen)
  2. Mit dem starken Gebrauch von IoC / DI in der heutigen Anwendungen ist es notwendig, Somethings neu zu konfigurieren und / oder Bootstrap-Code für abhängigen Code DI zu ändern.

Update:

Sorry, ich wusste nicht, dass der einzige Grund, dies war für mich zu brechen war, dass ich sie in Vorlage Einschränkungen verwendet werden.

Hinzufügen von Überlastung Methoden demise Standardparameter Verwendung

Art der Pause: Quelle-Ebene ruhig Semantik ändern

Da der Compiler wandelt Methode ruft mit fehlenden Standard-Parameterwerten auf einen expliziten Aufruf mit dem Standardwert auf der rufenden Seite, Kompatibilität für bestehende kompilierten Code gegeben ist; ein Verfahren, mit dem richtigen Signatur wird für alle zuvor kompilierten Code.

Auf der anderen Seite ruft ohne Verwendung von optionalen Parameter werden nun als Aufruf an die neue Methode zusammengestellt, die den optionalen Parameter fehlt. Alles arbeitet immer noch in Ordnung, aber wenn der angerufene Code in einer anderen Assembly befindet, neu kompilierten Code ruft es auf die neue Version dieser Versammlung jetzt abhängig ist. Bereitstellen von Baugruppen die Refactoring-Code aufrufen, ohne auch die Montage der Überarbeitete Code befindet sich in der Bereitstellung wird, was in „Methode nicht gefunden“ Ausnahmen.

API vor der Änderung

  public int MyMethod(int mandatoryParameter, int optionalParameter = 0)
  {
     return mandatoryParameter + optionalParameter;
  }    

API nach der Änderung

  public int MyMethod(int mandatoryParameter, int optionalParameter)
  {
     return mandatoryParameter + optionalParameter;
  }

  public int MyMethod(int mandatoryParameter)
  {
     return MyMethod(mandatoryParameter, 0);
  }

Beispielcode, der immer noch in Ordnung arbeiten werden

  public int CodeNotDependentToNewVersion()
  {
     return MyMethod(5, 6); 
  }

Beispielcode, der nun abhängig auf die neue Version ist beim Kompilieren

  public int CodeDependentToNewVersion()
  {
     return MyMethod(5); 
  }

Umbenennen eine Schnittstelle

Kinda von Break: Quelle und Binary

Sprachen Betroffene. Wahrscheinlich alle in C # getestet

API Vor Änderung:

public interface IFoo
{
    void Test();
}

public class Bar
{
    IFoo GetFoo() { return new Foo(); }
}

API Nach Wechsel:

public interface IFooNew // Of the exact same definition as the (old) IFoo
{
    void Test();
}

public class Bar
{
    IFooNew GetFoo() { return new Foo(); }
}

Beispiel Client-Code, das funktioniert aber gebrochen danach:

new Bar().GetFoo().Test(); // Binary only break
IFoo foo = new Bar().GetFoo(); // Source and binary break

Überlastung Methode mit einem Parameter von Nullable Types

Typ: Source-Level-Pause

Sprachen betroffen: C #, VB

API vor einer Änderung:

public class Foo
{
    public void Bar(string param);
}

API nach der Änderung:

public class Foo
{
    public void Bar(string param);
    public void Bar(int? param);
}

Beispiel Client-Code zu arbeiten, bevor die Änderung und gebrochen, nachdem es:

new Foo().Bar(null);

. Ausnahme: Der Aufruf ist nicht eindeutig zwischen den folgenden Methoden oder Eigenschaften

Förderung auf eine Erweiterungsmethode

Typ: Source-Level-Pause

Sprachen betroffen: (vielleicht andere) C # v6 und höher

API vor der Änderung:

public static class Foo
{
    public static void Bar(string x);
}

API nach der Änderung:

public static class Foo
{
    public void Bar(this string x);
}

Beispiel Client-Code vor der Änderung zu arbeiten und gebrochen, nachdem es:

using static Foo;

class Program
{
    static void Main() => Bar("hello");
}

Weitere Informationen: https://github.com/dotnet/csharplang/issues/665

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top