Ab C # 8.0 unter .NET Core 3.0 können Sie eine Implementierung definieren, wenn Sie ein Mitglied einer Schnittstelle deklarieren. Das häufigste Szenario besteht darin, Mitglieder sicher zu einer Schnittstelle hinzuzufügen, die bereits freigegeben und von unzähligen Clients verwendet wird.
In diesem Tutorial erfahren Sie, wie Sie:
- Erweitern Sie Schnittstellen sicher, indem Sie Methoden mit Implementierungen hinzufügen.
- Erstellen Sie parametrisierte Implementierungen, um mehr Flexibilität zu bieten.
- Ermöglichen Sie Implementierern, eine spezifischere Implementierung in Form einer Überschreibung bereitzustellen.

Voraussetzungen
Sie müssen Ihren Computer für die Ausführung von .NET Core einrichten, einschließlich des C # 8.0-Vorschau-Compilers. Der C # 8.0-Vorschau-Compiler ist ab Visual Studio 2019 oder dem neuesten .NET Core 3.0-Vorschau-SDK verfügbar. Standardschnittstellenmitglieder sind ab .NET Core 3.0-Vorschau 4 verfügbar.
Szenarioübersicht
Dieses Tutorial beginnt mit Version 1 einer Kundenbeziehungsbibliothek. Sie können die Starter-Anwendung auf unserem Beispiel-Repo auf GitHub herunterladen . Das Unternehmen, das diese Bibliothek erstellt hat, beabsichtigte, dass Kunden mit vorhandenen Anwendungen ihre Bibliothek übernehmen. Sie stellten den Benutzern ihrer Bibliothek minimale Schnittstellendefinitionen zur Verfügung, die sie implementieren konnten.
public interface ICustomer { IEnumerable<IOrder> PreviousOrders { get; } DateTime DateJoined { get; } DateTime? LastOrder { get; } string Name { get; } IDictionary<DateTime, string> Reminders { get; } }
Sie definierten eine zweite Schnittstelle, die eine Reihenfolge darstellt:
public interface IOrder { DateTime Purchased { get; } decimal Cost { get; } }
Über diese Schnittstellen könnte das Team eine Bibliothek für seine Benutzer erstellen, um eine bessere Erfahrung für ihre Kunden zu schaffen. Ihr Ziel war es, eine tiefere Beziehung zu bestehenden Kunden aufzubauen und ihre Beziehungen zu neuen Kunden zu verbessern.
Jetzt ist es Zeit, die Bibliothek für die nächste Version zu aktualisieren. Eine der angeforderten Funktionen ermöglicht einen Treuerabatt für Kunden mit vielen Bestellungen. Dieser neue Treuerabatt wird angewendet, wenn ein Kunde eine Bestellung aufgibt. Der spezifische Rabatt ist Eigentum jedes einzelnen Kunden. Jede Implementierung von ICustomer kann unterschiedliche Regeln für den Treuerabatt festlegen.
Der natürlichste Weg, diese Funktionalität hinzuzufügen, besteht darin, die ICustomer
Oberfläche mit einer Methode zu erweitern, mit der Treue-Rabatte ICustomer
können. Dieser Designvorschlag gab erfahrenen Entwicklern Anlass zur Sorge: "Schnittstellen sind nach ihrer Veröffentlichung unveränderlich! Das ist eine bahnbrechende Veränderung! “ C # 8.0 fügt Standardschnittstellenimplementierungen zum Aktualisieren von Schnittstellen hinzu. Die Autoren der Bibliothek können der Benutzeroberfläche neue Mitglieder hinzufügen und eine Standardimplementierung für diese Mitglieder bereitstellen.
Standardschnittstellenimplementierungen ermöglichen Entwicklern das Aktualisieren einer Schnittstelle, während Implementierer diese Implementierung weiterhin überschreiben können. Benutzer der Bibliothek können die Standardimplementierung als nicht unterbrechende Änderung akzeptieren. Wenn ihre Geschäftsregeln unterschiedlich sind, können sie überschreiben.
Upgrade mit Standard-Schnittstellenmitgliedern
Das Team einigte sich auf die wahrscheinlichste Standardimplementierung: einen Treuerabatt für Kunden.
Das Upgrade sollte die Funktionalität zum Festlegen von zwei Eigenschaften bieten: die Anzahl der Bestellungen, die für den Rabatt erforderlich sind, und den Prozentsatz des Rabatts. Dies macht es zu einem perfekten Szenario für Standardschnittstellenmitglieder. Sie können der ICustomer-Oberfläche eine Methode hinzufügen und die wahrscheinlichste Implementierung bereitstellen. Alle vorhandenen und alle neuen Implementierungen können die Standardimplementierung verwenden oder ihre eigene bereitstellen.
Fügen Sie zunächst die neue Methode zur Implementierung hinzu:
Der Autor der Bibliothek hat einen ersten Test geschrieben, um die Implementierung zu überprüfen:
SampleCustomer c = new SampleCustomer("customer one", new DateTime(2010, 5, 31)) { Reminders = { { new DateTime(2010, 08, 12), "childs's birthday" }, { new DateTime(1012, 11, 15), "anniversary" } } }; SampleOrder o = new SampleOrder(new DateTime(2012, 6, 1), 5m); c.AddOrder(o); o = new SampleOrder(new DateTime(2103, 7, 4), 25m); c.AddOrder(o);
Beachten Sie den folgenden Teil des Tests:
Diese SampleCustomer
von SampleCustomer
in ICustomer
ist erforderlich. Die SampleCustomer
Klasse muss keine Implementierung für ComputeLoyaltyDiscount
. ICustomer
von der ICustomer
Schnittstelle ICustomer
. Die SampleCustomer
Klasse erbt jedoch keine Mitglieder von ihren Schnittstellen. Diese Regel hat sich nicht geändert. Um eine in der Schnittstelle deklarierte und implementierte Methode aufzurufen, muss die Variable der Typ der Schnittstelle sein, in diesem Beispiel ICustomer
.
Parametrierung bereitstellen
Das ist ein guter Anfang. Die Standardimplementierung ist jedoch zu restriktiv. Viele Verbraucher dieses Systems wählen möglicherweise unterschiedliche Schwellenwerte für die Anzahl der Einkäufe, eine unterschiedliche Dauer der Mitgliedschaft oder einen anderen prozentualen Rabatt. Sie können mehr Kunden ein besseres Upgrade-Erlebnis bieten, indem Sie diese Parameter festlegen. Fügen wir eine statische Methode hinzu, mit der diese drei Parameter festgelegt werden, die die Standardimplementierung steuern:
In diesem kleinen Codefragment werden viele neue Sprachfunktionen gezeigt. Schnittstellen können jetzt statische Elemente enthalten, einschließlich Felder und Methoden. Verschiedene Zugriffsmodifikatoren sind ebenfalls aktiviert. Die zusätzlichen Felder sind privat, die neue Methode ist öffentlich. Alle Modifikatoren sind für Schnittstellenmitglieder zulässig.
Anwendungen, die die allgemeine Formel zur Berechnung des Treuerabattes verwenden, jedoch unterschiedliche Parameter verwenden, müssen keine benutzerdefinierte Implementierung bereitstellen. Sie können die Argumente über eine statische Methode festlegen. Mit dem folgenden Code wird beispielsweise eine "Kundenbewertung" festgelegt, mit der jeder Kunde mit einer Mitgliedschaft von mehr als einem Monat belohnt wird:
ICustomer.SetLoyaltyThresholds(new TimeSpan(30, 0, 0, 0), 1, 0.25m); Console.WriteLine($"Current discount: {theCustomer.ComputeLoyaltyDiscount()}");
Erweitern Sie die Standardimplementierung
Der Code, den Sie bisher hinzugefügt haben, bietet eine praktische Implementierung für Szenarien, in denen Benutzer beispielsweise die Standardimplementierung oder ein nicht verwandtes Regelwerk bereitstellen möchten. Als letztes Feature überarbeiten wir den Code ein wenig, um Szenarien zu ermöglichen, in denen Benutzer möglicherweise auf der Standardimplementierung aufbauen möchten.
Stellen Sie sich ein Startup vor, das neue Kunden gewinnen möchte. Sie bieten 50% Rabatt auf die erste Bestellung eines Neukunden. Andernfalls erhalten bestehende Kunden den Standardrabatt. Der Bibliotheksautor muss die Standardimplementierung in eine protected static
Methode verschieben, damit jede Klasse, die diese Schnittstelle implementiert, den Code in ihrer Implementierung wiederverwenden kann. Die Standardimplementierung des Schnittstellenmitglieds ruft auch diese gemeinsam genutzte Methode auf:
public decimal ComputeLoyaltyDiscount() => DefaultLoyaltyDiscount(this); protected static decimal DefaultLoyaltyDiscount(ICustomer c) { DateTime start = DateTime.Now - length; if ((c.DateJoined < start) && (c.PreviousOrders.Count() > orderCount)) { return discountPercent; } return 0; }
In einer Implementierung einer Klasse, die diese Schnittstelle implementiert, kann die Überschreibung die statische Hilfsmethode aufrufen und diese Logik erweitern, um den Rabatt "Neukunde" bereitzustellen:
public decimal ComputeLoyaltyDiscount() { if (PreviousOrders.Any() == false) return 0.50m; else return ICustomer.DefaultLoyaltyDiscount(this); }
Sie können den gesamten fertigen Code in unserem [Beispiel-Repo auf GitHub] sehen (Sie können die Starter-Anwendung auf unserem Beispiel-Repo auf GitHub herunterladen .
Diese neuen Funktionen bedeuten, dass Schnittstellen sicher aktualisiert werden können, wenn für diese neuen Mitglieder eine angemessene Standardimplementierung vorliegt. Entwerfen Sie Schnittstellen sorgfältig, um einzelne funktionale Ideen auszudrücken, die von mehreren Klassen implementiert werden können. Dies erleichtert das Aktualisieren dieser Schnittstellendefinitionen, wenn neue Anforderungen für dieselbe Funktionsidee entdeckt werden.