指南:在C#8.0中使用默认成员更新接口

从.NET Core 3.0上的C#8.0开始,在创建接口的成员时,您可以确定其实现。 最常见的情况是将成员安全地添加到已经由无数客户端发布和使用的接口中。

在本指南中,您将学习如何:


  • 通过添加带有实现的方法来扩展接口是安全的。
  • 创建参数化的实现以获得更大的灵活性。
  • 拥有实施更多具体实现并有权进行手动控制的权利。



从哪里开始?


首先,您需要配置计算机以使其与.NET Core一起使用,包括来自C#8.0预览版的编译器。 从Visual Studio 2019或更新的.NET Core 3.0预览SDK开始,可以使用这样的编译器。 默认接口成员从.NET Core 3.0(预览版4)开始可用。


方案概述


本教程从客户关系库的第一个版本开始。 您可以在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的每个实现,可以为忠诚度折扣设置不同的规则。

添加此功能的最方便方法是使用任何折扣扩展ICustomer界面。 该建议引起了有经验的开发人员的关注。 “接口在发布后是不变的! 这是一个关键的改变!” 在C#8.0中,添加了用于更新接口的默认接口实现。 图书馆作者可以添加新成员并默认实现它们


接口的默认实现允许开发人员更新接口,同时仍然允许其他开发人员覆盖此实现。 库用户可以接受默认实现作为非关键更改。


使用默认接口成员进行更新


团队同意最有可能的默认实现:降低客户忠诚度。


此更新必须可用于设置两个属性:获得折扣所需的订单数量和折扣百分比。 这使得它成为默认接口成员的理想脚本。 您可以将方法添加到ICustomer接口,并提供其最可能的实现。 所有现有的和任何新的实现都可以默认实现或具有自己的设置。


首先为实现添加一个新方法:


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

SampleCustomerICustomer这一部分很重要。 SampleCustomerSampleCustomer提供ComputeLoyaltyDiscount的实现; 这是由ICustomer界面提供的。 但是, SampleCustomerSampleCustomer从其接口继承成员。 此规则未更改。 要调用接口中实现的任何方法,该变量必须是接口类型,在此示例中为ICustomer


参数化


这是一个好的开始。 但是默认实现太有限了。 该系统的许多消费者可以为购买次数选择不同的阈值,不同的会员资格有效期或不同的折扣百分比,您可以通过提供一种设置这些参数的方法来改善更多客户的升级过程。 让我们添加一个静态方法来设置控制默认实现的这三个参数:


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

这小段代码显示了许多新的语言功能。 接口现在可以包括静态成员,包括字段和方法。 还包括各种访问修饰符。 其他字段是私有的,新方法是公共的。 接口成员可以使用任何修饰符。


使用通用公式计算忠诚度折扣但参数不同的应用程序不应提供自定义实现; 他们可以通过静态方法设置参数。 例如,以下代码设置“客户赞赏”,以奖励会员资格超过一个月的任何客户:


 ICustomer.SetLoyaltyThresholds(new TimeSpan(30, 0, 0, 0), 1, 0.25m); Console.WriteLine($"Current discount: {theCustomer.ComputeLoyaltyDiscount()}"); 

扩展默认实现


先前添加的代码为用户需要默认实现或提供不相关规则集的场景提供了便捷的实现。 对于最终版本,让我们重新组织一下代码,以包括用户可能希望依赖默认实现的方案。


考虑一家想吸引新客户的创业公司。 他们为新客户的第一笔订单提供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存储库中看到所有完成的代码。


这些新功能意味着如果新成员有可接受的默认实现,则可以安全地更新接口。 仔细设计接口,以表达可以由多个类实现的各个功能思想。 当发现相同功能思想的新要求时,这很容易更新这些接口定义。

Source: https://habr.com/ru/post/zh-CN456232/


All Articles