从 .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. 30441数据定义语言DDL

    数据定义:指对数据库对象的定义.删除和修改操作. 数据库对象主要包括数据表.视图.索引等. 数据定义功能通过CREATE.ALTER.DROP语句来完成. 按照操作对象分类来介绍数据定义的SQL语法. ...

  2. 关于vue项目中在js中引入图片问题

    <template> <div> <img v-for="(star,index) in stars" :src="star.src&quo ...

  3. ZooKeeper学习之路(五)—— ACL权限控制

    一.前言 为了避免存储在Zookeeper上的数据被其他程序或者人为误修改,Zookeeper提供了ACL(Access Control Lists)进行权限控制.只有拥有对应权限的用户才可以对节点进 ...

  4. 针对Linux 文件完整性监控的实现

    针对Linux 文件完整性监控的实现 摘要 计算机和互联网是20世纪以来最伟大的发明之一,随着计算机技术的不断发展,人们的生活方式发生了巨大的变化.计算机和互联网的发展给人们的生产生活带来了极大的便利 ...

  5. kafka 名词概念

    ProducerConsumerBrokerTopicPartitionConsumer Group分布式 Broker     Kafka集群包含一个或多个服务器,这种服务器被称为brokerTop ...

  6. Java内存区域(运行时数据区域)和内存模型(JMM)

    Java 内存区域和内存模型是不一样的东西,内存区域是指 Jvm 运行时将数据分区域存储,强调对内存空间的划分. 而内存模型(Java Memory Model,简称 JMM )是定义了线程和主内存之 ...

  7. Android 上传开源项目到 jcenter 实战踩坑之路

    本文微信公众号「AndroidTraveler」首发. 背景 其实 Android 上传开源项目到 jcenter 并不是一件新鲜事,网上也有很多文章. 包括我本人在将开源项目上传到 jcenter ...

  8. Codeforces 348B:Apple Tree(DFS+LCM+思维)

    http://codeforces.com/contest/348/problem/B 题意:给一棵树,每个叶子结点有w[i]个苹果,每个子树的苹果数量为该子树所有叶子结点苹果数量之和,要使得每个结点 ...

  9. ArrayList的add方法实现

    ArrayList的底层是由数组实现,所以所有的操作都是围绕数组展开,要想理解add方法,就得先了解数组的增加,所以我们先实现一个数组的add,数组的添加可以从尾部增加或者其他位置插入, 如果在数组的 ...

  10. 数据结构与算法分析(C++)读书笔记

    第一章 综述 数学知识复习: 指数.对数.级数.模运算.证明方法(证明数据结构分析几轮最常用方法:归纳法.反证法) 递归简论 C++知识:类.细节.模板.矩阵.