2016-04-08 11 views
1

Ich lerne gerade über C# -Erweiterungsmethoden. Ich habe an einigen Stellen gelesen, dass das Hinzufügen von Membern zu Klassen die Rückwärtskompatibilität für Code reduziert, der diese Klassen verwendet.Wie reduziert das Hinzufügen von Mitgliedern die Rückwärtskompatibilität von Klassen?

Ich habe dies hier lesen: https://blogs.msdn.microsoft.com/vbteam/2007/03/10/extension-methods-best-practices-extension-methods-part-6/

Und Seite 418 von Pro C# Buch Troelson.

Ich fürchte, das macht keinen Sinn für mich. Sicherlich wird jeder Code, der Instanzen dieser Klassen verwendet, wie sie vor dem Hinzufügen zusätzlicher Member verwendet wurden (ohne Erweiterungsmethoden zu verwenden, nur indem sie zur Klasse hinzugefügt werden), immer noch alle alten Methoden, Eigenschaften, Felder und Konstruktoren aufrufen können vorher, wie sie sich nicht geändert haben. Selbst wenn die neuen Mitglieder den Status des Objekts ändern können, werden sie niemals im alten Code aufgerufen, daher ist der Code abwärtskompatibel.

Was sehe ich hier nicht?

+1

Sie sehen nicht, wie sich das Ändern des Codes, der beim Aufruf einer bestimmten Methode für eine Instanz eines Typs von einer Implementierung in eine andere ausgeführt wird, möglicherweise auf diesen Code auswirkt? – Servy

+0

Ich habe es nicht als Ändern des Codes gesehen, eher zur Klasse hinzufügen. Natürlich kann ich sehen, dass es gefährlich ist, mit Code zu hantieren, von dem andere Klassen abhängig sind. Das Hinzufügen einer Methodenüberladung, die Vorrang vor der ursprünglichen Methode für bestimmte Parameter hat (die in den alten Codemethodenaufrufen verwendet werden könnte), wie in der unten akzeptierten Antwort, ist jedoch ein klares Beispiel dafür, was genau schief gehen kann, was ich nicht getan habe gedacht an. –

+0

@GeorgeWooding, Ein zweites Beispiel zu meiner Antwort hinzugefügt. – devuxer

Antwort

2

Hier ist ein möglicher Weg, eine neue Methode Hinzufügen könnte tatsächlich Client-Code brechen ...

void Main() 
{ 
    var oldFoo = new OldFoo(); 
    var oldResult = oldFoo.Calculate(2, 2); // 4 
    var newFoo = new NewFoo(); 
    var newResult = newFoo.Calculate(2, 2); // 0 
} 

public class OldFoo 
{ 
    public int Calculate(params int[] values) 
    { 
     return values.Sum(); 
    } 
} 

public class NewFoo 
{ 
    public int Calculate(params int[] values) 
    { 
     return values.Sum(); 
    } 

    public int Calculate(int value1, int value2) 
    { 
     return value1 - value2; 
    } 
} 

Und hier ist eine andere Art und Weise, mit einem Verlängerungsverfahren speziell den Umgang ...

Anfangs definiert der Client eine Erweiterungsmethode, um Foo die Fähigkeit Combine:

void Main() 
{ 
    var foo = new Foo(); 
    var result = foo.Combine(2, 2); // "22" 
} 

public static class Extensions // added by client 
{ 
    public static string Combine(this Foo foo, params int[] values) 
    { 
     return string.Join(string.Empty, values.Select(x => x.ToString())); 
    } 
} 

public class Foo { } 
zu geben

Später fügt der Entwickler Foo eine neue Combine Methode zur Klasse:

void Main() 
{ 
    var foo = new Foo(); 
    var result = foo.Combine(2, 2); // 4 
} 

public static class Extensions 
{ 
    public static string Combine(this Foo foo, params int[] values) 
    { 
     return string.Join(string.Empty, values.Select(x => x.ToString())); 
    } 
} 

public class Foo 
{ 
    public int Combine(params int[] values) 
    { 
     return values.Sum(); 
    } 
} 

Beachten Sie, dass die Erweiterungsmethode oder durch die neue Combine Instanz-Methode beschattet effektiv blockiert wird.

-1

Eine Analogie in der realen Welt könnte helfen. Denken Sie darüber nach wie Maschinen. Stellen Sie sich vor, jemand entwirft einen Motor, den die Leute als Grundlage für eine Ausrüstung verwenden, wie etwa eine Erntemaschine. Wenn der Motorkonstrukteur entscheidet, dass ein Kraftstofffilter hilfreich wäre und fügt das hinzu, könnte dies das Design des Harvesters ruinieren, weil dieses Design etwas in den Raum gebracht haben könnte, der jetzt von dem neuen Kraftstofffilter eingenommen wird.

Das Hinzufügen des neuen Elements (der Kraftstoffpumpe) verringerte die Abwärtskompatibilität. Das Harvester-Design basierte auf einer in der Geschichte zurückliegenden Version.

Und ein programmierbasiertes Beispiel: Ein App-Designer erstellt einen Parser für seine Anwendung, der sich in einer bestimmten Weise verhält. Nach der Veröffentlichung wurde festgestellt, dass der Parser die Spezifikation für einige ungewöhnliche Bedingungen nicht korrekt implementiert. Eine neue Version wird veröffentlicht, die die Spezifikation korrekt implementiert, aber ein Flag hinzufügt, um das vorherige Verhalten bereitzustellen.

1

Der Punkt ist, dass Erweiterungsmethoden den gleichen Namespace mit Member-Methoden teilen können, und wenn dies der Fall ist, haben Member-Methoden Vorrang vor dem Namen. Die Folge davon ist, dass Sie als Bibliotheksentwickler den Code eines Clients brechen können, der in Ihrer eigenen Anwendung eine Erweiterungsmethode für Ihre Klasse eingeführt hat. Ohne dass du wissen kannst, dass du es tust.

Wenn Sie Ihre Bibliotheksklasse mit einer neuen Mitgliedsmethode aktualisieren und Ihr Client das Update installiert, kann es sein, dass Ihre neue Methode den gleichen Namen wie die zuvor hinzugefügte Erweiterungsmethode hat. Oder er findet es möglicherweise nicht, wenn die Argumentlisten kompatibel sind. Seine Erweiterungsmethode wird nun von Ihrer neuen Mitgliedermethode ausgeblendet. Sein Code wird nun entweder nicht kompiliert (inkompatible Argumentliste), oder, schlimmer noch, er verhält sich anders.