C#8.0 中使用默認接口成員更新接口

從 .NET Core 3.0 上的 C# 8.0 開始,能夠在聲明接口成員時定義實現。 最多見的方案是安全地將成員添加到已經由無數客戶端發佈並使用的接口。html

在本教程中,你將瞭解:git

  • 經過使用實現添加方法,安全地擴展接口。
  • 建立參數化實現以提供更大的靈活性。
  • 使實現器可以以替代的形式提供更具體的實現。

 

01 系統必備

須要將計算機設置爲運行 .NET Core,包括 C# 8.0 預覽版編譯器。 從 Visual Studio 2019 或最新的 .NET Core 3.0 預覽版 SDK 開始,可使用 C# 8.0 預覽版編譯器。 從 .NET Core 3.0 預覽版 4 開始提供默認接口成員。github

02 方案概述
本教程從客戶關係庫版本 1 開始。 能夠在 GitHub 上的示例存儲庫中獲取入門應用程序。生成此庫的公司但願擁有現有應用程序的客戶採用其庫。 他們爲使用其庫的用戶提供最小接口定義供其實現。 如下是客戶的接口定義:
public interface ICustomer
{
    IEnumerable<IOrder> PreviousOrders { get; }

    DateTime DateJoined { get; }
    DateTime? LastOrder { get; }
    string Name { get; }
    IDictionary<DateTime, string> Reminders { get; }
}

他們定義了表示訂單的第二個接口:安全

public interface IOrder
{
    DateTime Purchased { get; }
    decimal Cost { get; }
}

經過這些接口,團隊能夠爲其用戶生成一個庫,以便爲其客戶創造更好的體驗。 他們的目標是與現有客戶創建更深刻的關係,並改善他們與新客戶的關係。測試

如今,是時候爲下一版本升級庫了。 其中一個請求的功能能夠爲擁有大量訂單的客戶提供忠實客戶折扣。 不管客戶什麼時候下單,都會應用這一新的忠實客戶折扣。 該特定折扣是每位客戶的財產。 ICustomer 的每一個實現均可覺得忠實客戶折扣設置不一樣的規則。this

添加此功能的最天然方式是使用用於應用任何忠實客戶折扣的方法來加強 ICustomer 接口。 此設計建議引發了經驗豐富的開發人員的關注:「一旦發佈,接口就是固定不變的! 這是一項突破性的變革!」 C# 8.0 添加了默認接口實現 用於升級接口。 庫做者能夠向接口添加新成員,併爲這些成員提供默認實現。spa

默認接口實現使開發人員可以升級接口,同時仍容許任何實現器替代該實現。 庫的用戶能夠接受默認實現做爲非中斷性變動。 若是他們的業務規則不一樣,則能夠進行替代。設計

03 使用默認接口成員升級

團隊就最有可能的默認實現達成一致:針對客戶的忠實客戶折扣。code

升級應提供用於設置兩個屬性的功能:符合折扣條件所需的訂單數量以及折扣百分比。 這使其成爲用於默認接口成員的完美方案。 能夠向 ICustomer 接口添加方法,並提供最有可能的實現。 全部現有的和任何新的實現均可以使用默認實現,或者提供其本身的實現。htm

首先,將新方法添加到實現中:

// Version 1:
public decimal ComputeLoyaltyDiscount()
{
    DateTime TwoYearsAgo = DateTime.Now.AddYears(-2);
    if ((DateJoined < TwoYearsAgo) && (PreviousOrders.Count() > 10))
    {
        return 0.10m;
    }
    return 0;
}

庫做者編寫了用於檢查實現的第一個測試:

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); // 檢查折扣 ICustomer theCustomer = c; Console.WriteLine($"Current discount: {theCustomer.ComputeLoyaltyDiscount()}");

注意測試的如下部分:

// 檢查折扣
ICustomer theCustomer = c;
Console.WriteLine($"Current discount: {theCustomer.ComputeLoyaltyDiscount()}");

從 SampleCustomer 到 ICustomer 的強制轉換是必需的。 SampleCustomer 類不須要爲 ComputeLoyaltyDiscount 提供實現;這由 ICustomer 接口提供。 可是,SampleCustomer 類不會從其接口繼承成員。 該規則沒有更改。 若要調用在接口中聲明和實現的任何方法,該變量的類型必須是接口的類型,在本示例中爲 ICustomer

04 提供參數化
這是一個好的開始。 可是,默認實現存在太多限制。 此係統的許多使用者可能會選擇不一樣的購買數量閾值、不一樣的會員資格時長或不一樣的折扣百分比。 經過提供用於設置這些參數的方法,可爲更多客戶提供更好的升級體驗。 讓咱們添加一個靜態方法,該方法可設置控制默認實現的三個參數:
// 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); // 2年 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; }

這個小代碼片斷中展現了許多新的語言功能。 接口如今能夠包含靜態成員,其中包括字段和方法。 還啓用了不一樣的訪問修飾符。 其餘字段是專用的,新方法是公共的。 接口成員容許使用任何修飾符。

使用常規公式計算忠實客戶折扣但參數有所不一樣的應用程序不須要提供自定義實現;它們能夠經過靜態方法設置自變量。 例如,如下代碼設置「客戶答謝」,獎勵任何成爲會員超過一個月的客戶:

ICustomer.SetLoyaltyThresholds(new TimeSpan(30, 0, 0, 0), 1, 0.25m);
Console.WriteLine($"Current discount: {theCustomer.ComputeLoyaltyDiscount()}");
05 擴展默認實現

目前添加的代碼提供了方便的實現,可用於用戶須要相似默認實現的項目的方案,或用於提供一組不相關的規則。 對於最後一個功能,讓咱們稍微重構一下代碼,以實現用戶可能須要基於默認實現進行生成的方案。

假設有一家想要吸引新客戶的初創企業。 他們爲新客戶的第一筆訂單提供 50% 的折扣, 而現有客戶則會得到標準折扣。 庫做者須要將默認實現移入 protected static 方法,以便實現此接口的任何類均可以在其實現中重用代碼。 接口成員的默認實現也調用此共享方法:

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

在實現此接口的類的實現中,替代能夠調用靜態幫助程序方法,並擴展該邏輯以提供「新客戶」折扣:

public decimal ComputeLoyaltyDiscount()
{
   if (PreviousOrders.Any() == false)
        return 0.50m;
    else
        return ICustomer.DefaultLoyaltyDiscount(this);
}

能夠在咱們位於 [GitHub 上的示例存儲庫]中查看整個完成的代碼(能夠在 GitHub 上的示例存儲庫中獲取入門應用程序)。

這些新功能意味着,當這些新成員擁有合理的默認實現時,接口能夠安全地更新。 精心設計接口,以表達可由多個類實現的單個功能概念。 這樣一來,在發現針對同一功能概念的新要求時,能夠更輕鬆地升級這些接口定義。

相關文章
相關標籤/搜索