Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Sie können eine Implementierung definieren, wenn Sie ein Mitglied einer Schnittstelle deklarieren. Das häufigste Szenario ist das sichere Hinzufügen von Mitgliedern zu einer bereits freigegebenen und von innumerierbaren Clients verwendeten Schnittstelle.
In diesem Tutorial lernen 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 zu bieten.
Voraussetzungen
Sie müssen Ihren Computer für das Ausführen von .NET einschließlich des C#-Compilers einrichten. Der C#-Compiler ist mit Visual Studio 2022- oder dem .NET SDK-verfügbar.
Beschreibung des Szenarios
Dieses Lernprogramm beginnt mit Version 1 einer Kundenbeziehungsbibliothek. Sie können die Startanwendung auf unserem Beispiel-Repository auf GitHub abrufen. Das Unternehmen, das diese Bibliothek erstellt hat, beabsichtigte, dass Kunden mit vorhandenen Anwendungen seine Bibliothek verwenden. Sie haben minimale Schnittstellendefinitionen für Benutzer ihrer Bibliothek bereitgestellt, die implementiert werden können. Dies ist die Schnittstellendefinition für einen Kunden:
public interface ICustomer
{
IEnumerable<IOrder> PreviousOrders { get; }
DateTime DateJoined { get; }
DateTime? LastOrder { get; }
string Name { get; }
IDictionary<DateTime, string> Reminders { get; }
}
Sie haben eine zweite Schnittstelle definiert, die eine Reihenfolge darstellt:
public interface IOrder
{
DateTime Purchased { get; }
decimal Cost { get; }
}
Über diese Schnittstellen konnte das Team eine Bibliothek für ihre Benutzer erstellen, um eine bessere Benutzererfahrung für ihre Kunden zu schaffen. Ziel war es, eine tiefere Beziehung zu bestehenden Kunden zu schaffen und ihre Beziehungen zu neuen Kunden zu verbessern.
Jetzt ist es an der Zeit, die Bibliothek für die nächste Version zu aktualisieren. Eine der angeforderten Features ermöglicht einen Treuerabatt für Kunden, die viele Bestellungen haben. Dieser neue Treuerabatt wird immer dann angewendet, wenn ein Kunde eine Bestellung abhebt. Der spezifische Rabatt ist eine Eigenschaft jedes einzelnen Kunden. Jede Implementierung von ICustomer
kann andere Regeln für den Treuerabatt festlegen.
Die natürlichste Möglichkeit, diese Funktionalität hinzuzufügen, besteht darin, die ICustomer
Schnittstelle mit einer Methode zu verbessern, um alle Treuerabatte anzuwenden. Dieser Entwurfsvorschlag verursachte Bedenken bei erfahrenen Entwicklern: "Schnittstellen sind unveränderlich, sobald sie veröffentlicht wurden! Machen Sie keine bruchbrechenden Änderungen!" Sie sollten Standardschnittstellenimplementierungen für das Upgrade von Schnittstellen verwenden. Die Bibliotheksautoren können der Schnittstelle neue Member hinzufügen und eine Standardimplementierung für diese Mitglieder bereitstellen.
Standardschnittstellenimplementierungen ermöglichen Entwicklern das Upgrade einer Schnittstelle, während alle Implementierer diese Implementierung außer Kraft setzen können. Benutzer der Bibliothek können die Standardimplementierung als ungebrochene Änderung akzeptieren. Wenn ihre Geschäftsregeln anders sind, können sie überschreiben.
Upgrade mit Standardschnittstellenmethoden
Das Team vereinbarte die wahrscheinlichste Standardimplementierung: einen Treuerabatt für Kunden.
Das Upgrade sollte die Funktionalität bereitstellen, um zwei Eigenschaften festzulegen: die Anzahl der Bestellungen, die für den Rabatt erforderlich sind, und den Prozentsatz des Rabatts. Diese Features machen es zu einem perfekten Szenario für Standardschnittstellenmethoden. Sie können der ICustomer
Schnittstelle eine Methode hinzufügen und die wahrscheinlichste Implementierung bereitstellen. Alle vorhandenen und alle neuen Implementierungen können die Standardimplementierung verwenden oder ihre eigenen bereitstellen.
Fügen Sie zunächst die neue Methode zur Schnittstelle hinzu, einschließlich des Textkörpers der Methode:
// Version 1:
public decimal ComputeLoyaltyDiscount()
{
DateTime TwoYearsAgo = DateTime.Now.AddYears(-2);
if ((DateJoined < TwoYearsAgo) && (PreviousOrders.Count() > 10))
{
return 0.10m;
}
return 0;
}
Der Bibliotheksautor 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);
// Check the discount:
ICustomer theCustomer = c;
Console.WriteLine($"Current discount: {theCustomer.ComputeLoyaltyDiscount()}");
Beachten Sie den folgenden Teil des Tests:
// Check the discount:
ICustomer theCustomer = c;
Console.WriteLine($"Current discount: {theCustomer.ComputeLoyaltyDiscount()}");
Diese Umwandlung von SampleCustomer
zu ICustomer
ist erforderlich. Die SampleCustomer
Klasse muss keine Implementierung für ComputeLoyaltyDiscount
bereitstellen; dies wird durch die ICustomer
Schnittstelle bereitgestellt. Allerdings erbt die SampleCustomer
-Klasse keine Member von ihren Schnittstellen. Diese Regel wurde nicht geändert. Um eine in der Schnittstelle deklarierte und implementierte Methode aufzurufen, muss die Variable der Typ der Schnittstelle ICustomer
in diesem Beispiel sein.
Parameterisierung bereitstellen
Die Standardimplementierung ist zu restriktiv. Viele Verbraucher dieses Systems können unterschiedliche Schwellenwerte für die Anzahl der Einkäufe, eine andere Mitgliedschaftsdauer oder einen anderen Prozentualen Rabatt auswählen. Sie können eine bessere Upgradeerfahrung für weitere Kunden bieten, indem Sie eine Möglichkeit zum Festlegen dieser Parameter bereitstellen. Fügen wir nun eine statische Methode hinzu, mit der diese drei Parameter festgelegt werden, die die Standardimplementierung steuern:
// Version 2:
public static void SetLoyaltyThresholds(
TimeSpan ago,
int minimumOrders = 10,
decimal percentageDiscount = 0.10m)
{
length = ago;
orderCount = minimumOrders;
discountPercent = percentageDiscount;
}
private static TimeSpan length = new TimeSpan(365 * 2, 0,0,0); // two years
private static int orderCount = 10;
private static decimal discountPercent = 0.10m;
public decimal ComputeLoyaltyDiscount()
{
DateTime start = DateTime.Now - length;
if ((DateJoined < start) && (PreviousOrders.Count() > orderCount))
{
return discountPercent;
}
return 0;
}
In diesem kleinen Codefragment werden viele neue Sprachfunktionen angezeigt. Schnittstellen können jetzt statische Elemente enthalten, einschließlich Feldern und Methoden. Verschiedene Zugriffsmodifizierer sind ebenfalls aktiviert. Die anderen Felder sind privat, die neue Methode ist öffentlich. Beliebige der Modifizierer sind auf Schnittstellenmembern erlaubt.
Anwendungen, die die allgemeine Formel zum Berechnen des Treuerabatts verwenden, aber verschiedene Parameter, müssen keine benutzerdefinierte Implementierung bereitstellen; sie können die Argumente über eine statische Methode festlegen. Der folgende Code legt beispielsweise eine "Kundenwürdigung" fest, die jeden Kunden mit mehr als einem Monat Mitgliedschaft belohnt:
ICustomer.SetLoyaltyThresholds(new TimeSpan(30, 0, 0, 0), 1, 0.25m);
Console.WriteLine($"Current discount: {theCustomer.ComputeLoyaltyDiscount()}");
Erweitern der Standardimplementierung
Der Code, den Sie bisher hinzugefügt haben, hat eine bequeme Implementierung für diese Szenarien bereitgestellt, in denen Benutzer etwas wie die Standardimplementierung wünschen oder einen nicht verknüpften Satz von Regeln bereitstellen möchten. Für ein endgültiges Feature sollten wir den Code etwas umgestalten, um Szenarien zu ermöglichen, in denen Benutzer möglicherweise auf der Standardimplementierung aufbauen möchten.
Betrachten Sie ein Startup, das neue Kunden gewinnen möchte. Sie bieten einen Rabatt von 50% bei der ersten Bestellung eines neuen Kunden an. 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 diese freigegebene Methode ebenfalls 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;
}
Bei einer Klasse, die diese Schnittstelle implementiert, kann die Überschreibung die statische Hilfsmethode aufrufen und diese Logik erweitern, um den Rabatt für "neue Kunden" 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-Repository auf GitHub sehen. Sie können die Startanwendung auf unserem Beispiel-Repository auf GitHub abrufen.
Diese neuen Features bedeuten, dass Schnittstellen sicher aktualisiert werden können, wenn es eine angemessene Standardimplementierung für diese neuen Member gibt. Entwerfen Sie sorgfältig Schnittstellen, um einzelne funktionale Ideen auszudrücken, die von mehreren Klassen implementiert werden. Dies erleichtert das Upgrade dieser Schnittstellendefinitionen, wenn neue Anforderungen für dieselbe funktionale Idee entdeckt werden.