从 .NET Core 3.0 上的 C# 8.0 开始,可以在声明接口成员时定义实现。 最常见的方案是安全地将成员添加到已经由无数客户端发布并使用的接口。

在本教程中,你将了解:

  • 通过使用实现添加方法,安全地扩展接口。
  • 创建参数化实现以提供更大的灵活性。
  • 使实现器能够以替代的形式提供更具体的实现。
01 系统必备

需要将计算机设置为运行 .NET Core,包括 C# 8.0 预览版编译器。 从 Visual Studio 2019 或最新的 .NET Core 3.0 预览版 SDK 开始,可以使用 C# 8.0 预览版编译器。 从 .NET Core 3.0 预览版 4 开始提供默认接口成员。

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 的每个实现都可以为忠实客户折扣设置不同的规则。

添加此功能的最自然方式是使用用于应用任何忠实客户折扣的方法来增强 ICustomer 接口。 此设计建议引起了经验丰富的开发人员的关注:“一旦发布,接口就是固定不变的! 这是一项突破性的变革!” C# 8.0 添加了默认接口实现 用于升级接口。 库作者可以向接口添加新成员,并为这些成员提供默认实现。

默认接口实现使开发人员能够升级接口,同时仍允许任何实现器替代该实现。 库的用户可以接受默认实现作为非中断性变更。 如果他们的业务规则不同,则可以进行替代。

03 使用默认接口成员升级

团队就最有可能的默认实现达成一致:针对客户的忠实客户折扣。

升级应提供用于设置两个属性的功能:符合折扣条件所需的订单数量以及折扣百分比。 这使其成为用于默认接口成员的完美方案。 可以向 ICustomer 接口添加方法,并提供最有可能的实现。 所有现有的和任何新的实现都可以使用默认实现,或者提供其自己的实现。

首先,将新方法添加到实现中:

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

库作者编写了用于检查实现的第一个测试:

SampleCustomer c = new SampleCustomer("customer one", new DateTime(, , ))
{
Reminders =
{
{ new DateTime(, , ), "childs's birthday" },
{ new DateTime(, , ), "anniversary" }
}
};
SampleOrder o = new SampleOrder(new DateTime(, , ), 5m);
c.AddOrder(o); o = new SampleOrder(new DateTime(, , ), 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 = , decimal percentageDiscount = 0.10m)
{
length = ago;
orderCount = minimumOrders;
discountPercent = percentageDiscount;
}
private static TimeSpan length = new TimeSpan( * , ,,); // 2年
private static int orderCount = ;
private static decimal discountPercent = 0.10m; public decimal ComputeLoyaltyDiscount()
{
DateTime start = DateTime.Now - length; if ((DateJoined < start) && (PreviousOrders.Count() > orderCount))
{
return discountPercent;
}
return ;
}

这个小代码片段中展示了许多新的语言功能。 接口现在可以包含静态成员,其中包括字段和方法。 还启用了不同的访问修饰符。 其他字段是专用的,新方法是公共的。 接口成员允许使用任何修饰符。

使用常规公式计算忠实客户折扣但参数有所不同的应用程序不需要提供自定义实现;它们可以通过静态方法设置自变量。 例如,以下代码设置“客户答谢”,奖励任何成为会员超过一个月的客户:

ICustomer.SetLoyaltyThresholds(new TimeSpan(, , , ), , 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 ;
}

在实现此接口的类的实现中,替代可以调用静态帮助程序方法,并扩展该逻辑以提供“新客户”折扣:

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

可以在我们位于 [GitHub 上的示例存储库]中查看整个完成的代码(可以在 GitHub 上的示例存储库中获取入门应用程序)。

这些新功能意味着,当这些新成员拥有合理的默认实现时,接口可以安全地更新。 精心设计接口,以表达可由多个类实现的单个功能概念。 这样一来,在发现针对同一功能概念的新要求时,可以更轻松地升级这些接口定义。

C#8.0 中使用默认接口成员更新接口的更多相关文章

  1. C# 8.0 中开启默认接口实现

    原文:C# 8.0 中开启默认接口实现 当你升级到 C# 8.0 和 .NET Core 3.0 之后,你就可以开始使用默认接口实现的功能了. 从现在开始,你可以在接口里面添加一些默认实现的成员,避免 ...

  2. 在Tomcat7.0中设置默认服务器和不加端口名访问

    前言 昨天买了域名,服务器,然后搭建了环境,然后想他通过默认的端口,不用端口就访问. 设置WEB项目的欢迎页 在WEB-INF文件夹下有个web.xml文件(最近新建的项目不包含此文件,可以手动新建) ...

  3. Springboot2.0中jpa默认创建的mysql表为myisam引擎问题

    使用Springboot2.0后,使用jpa操作mysql数据库时,默认创建的表的引擎是myisam,myisam是不能加外键的,找了一些资源,最终可以用此方法解决! yml格式: spring: j ...

  4. Vue.js2.0中的变化(持续更新中)

    最近自己在学习Vue.js,在看一些课程的时候可能Vue更新太块了导致课程所讲知识和现在Vue的版本不符,从而报错,我会在以后的帖子持续更新Vue的变化与更新,大家也可以一起交流,共同监督学习! 1. ...

  5. IHostingEnvironment VS IHostEnvironment - .NET Core 3.0中的废弃类型

    原文: https://andrewlock.net/ihostingenvironment-vs-ihost-environment-obsolete-types-in-net-core-3/ 作者 ...

  6. asp.net core 3.0 中使用 swagger

    asp.net core 3.0 中使用 swagger Intro 上次更新了 asp.net core 3.0 简单的记录了一下 swagger 的使用,那个项目的 api 比较简单,都是匿名接口 ...

  7. [译]C#8.0中一个使接口更加灵活的新特性-默认接口实现

    9月份的时候,微软宣布正式发布C#8.0,作为.NET Core 3.0发行版的一部分.C#8.0的新特性之一就是默认接口实现.在本文中,我们将一起来聊聊默认接口实现. 众所周知,对现有应用程序的接口 ...

  8. JDK8.0接口中的默认方法和静态方法

    我们在接口中通常定义的方法是抽象方法,即没有方法体,只有返回值类型和方法名:(public abstract) void Method(); 类在实现接口的时候必须重写抽象方法才可以 jdk8中新加的 ...

  9. Java接口成员变量和方法默认修饰符

     Java的interface中,成员变量的默认修饰符为:public static final 所以我们在interface中定义成员变量的时候,可以 1:public static final S ...

随机推荐

  1. 百度网盘背后的存储系统atlas

    原文  http://www.bitstech.net/2015/07/25/baidu-atlas/   百度网盘免费提供2TB存储, 它的存储量一定是惊人的, 支持它的存储系统atlas也是相当不 ...

  2. Eric Linux - 1 Basic concepts of linux

    Computer basic Computer 5 parts CPU Input Output Memory External storage device. CPU RISC: Reduced I ...

  3. 每日一问:浅谈 onAttachedToWindow 和 onDetachedFromWindow

    基本上所有 Android 开发都会接触到 onCreate().onDestory().onStart().onStop() 等这些生命周期方法,但却不是所有人都会去关注到 onAttachXXX( ...

  4. 附005.Kubernetes身份认证

    一 Kubernetes访问 1.1 Kubernetes交互 与Kubernetes交互通常有kubectl.客户端(Dashboard).REST API请求. 1.2 API访问流程 用户使用k ...

  5. MySQL8.0 DDL原子性特性

    1. DDL原子性概述 8.0之前并没有统一的数据字典dd,server层和引擎层各有一套元数据,sever层的元数据包括(.frm,.opt,.par,.trg等),用于存储表定义,分区表定义,触发 ...

  6. 一步到位安装Centos7、配置VMware、连接Xshell

    1.创建虚拟机 1.0 创建新的虚拟机 1.0.1 选择自定义配置 打开VMware,点击创建新的虚拟机. 如下图所示:   1.0.2 选择虚拟机硬件兼容性 如下图所示:   1.0.3 安装客户操 ...

  7. Android 即时通讯开发小结(二)

    <Android 即时通讯开发小结>基于IM Andriod 开发的各种常见问题,结合网易云信即时通讯技术的实践,对IM 开发做一个全面的总结. 相关推荐阅读:. Android 即时通讯 ...

  8. JavaWeb入门_模仿天猫整站Tmall_JavaEE实践项目

    Tmall_JavaEE 技术栈 Servlet + Jsp + Tomcat , 是Java Web入门非常好的练手项目 效果展示: 模仿天猫前台 模仿天猫后台 项目简介 关联项目 github - ...

  9. Niginx简单的配置

    #user nobody; #这里的数值不能超过 CPU 的总核数,因为在单个核上部署超过 1 个 Nginx 服务进程并不起到提高性能的作用.worker_processes 2; #nginx进程 ...

  10. Spring Bean 生命周期之“我从哪里来?” 懂得这个很重要

    Spring bean 的生命周期很容易理解.实例化 bean 时,可能需要执行一些初始化以使其进入可用 (Ready for Use)状态.类似地,当不再需要 bean 并将其从容器中移除时,可能需 ...