.c# 调用接口_教程:使用C#8.0中的默认接口成员更新接口

.c# 调用接口

Beginning with C# 8.0 on .NET Core 3.0, you can define an implementation when you declare a member of an interface. The most common scenario is to safely add members to an interface already released and used by innumerable clients.

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

In this tutorial, you'll learn how to:

在本教程中,您将学习如何:

  • Extend interfaces safely by adding methods with implementations.

    通过添加带有实现的方法来安全地扩展接口。
  • Create parameterized implementations to provide greater flexibility.

    创建参数化的实现以提供更大的灵活性。
  • Enable implementers to provide a more specific implementation in the form of an override.

    使实现者能够以覆盖的形式提供更具体的实现。

先决条件 (Prerequisites)

You’ll need to set up your machine to run .NET Core, including the C# 8.0 preview compiler. The C# 8.0 preview compiler is available starting with Visual Studio 2019, or the latest .NET Core 3.0 preview SDK. Default interface members are available beginning with .NET Core 3.0 preview 4.

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

方案概述 (Scenario overview)

This tutorial starts with version 1 of a customer relationship library. You can get the starter application on our samples repo on GitHub. The company that built this library intended customers with existing applications to adopt their library.They provided minimal interface definitions for users of their library to implement.Here's the interface definition for a customer:

本教程从客户关系库的版本1开始。 您可以在GitHub示例存储库中获取starter应用程序。 构建该库的公司旨在让客户使用现有的应用程序来采用他们的库,他们为库的用户提供了最少的接口定义以供实施,这是客户的接口定义:

public interface ICustomer
{
    IEnumerable<IOrder> PreviousOrders { get; }

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

They defined a second interface that represents an order:

他们定义了代表订单的第二个接口:

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

From those interfaces, the team could build a library for their users to create a better experience for their customers. Their goal was to create a deeper relationship with existing customers and improve their relationships with new customers.

通过这些界面,团队可以为其用户构建一个库,以为其客户创造更好的体验。 他们的目标是与现有客户建立更深的关系,并改善与新客户的关系。

Now, it's time to upgrade the library for the next release. One of the requested features enables a loyalty discount for customers that have lots of orders. This new loyalty discount gets applied whenever a customer makes an order. The specific discount is a property of each individual customer. Each implementation of ICustomer can set different rules for the loyalty discount.

现在,是时候为下一个版本升级该库了。 所请求的功能之一为具有大量订单的客户提供忠诚度折扣。 每当客户下订单时,都会应用此新的忠诚度折扣。 具体折扣是每个客户的财产。 ICustomer的每个实现都可以为忠诚度折扣设置不同的规则。

The most natural way to add this functionality is to enhance the ICustomerinterface with a method to apply any loyalty discount. This design suggestion caused concern among experienced developers: «Interfaces are immutable once they've been released! This is a breaking change!» C# 8.0 adds default interface implementations for upgrading interfaces. The library authors can add new members to the interface and provide a default implementation for those members.

添加此功能的最自然的方法是通过应用任何忠诚度折扣的方法来增强ICustomer界面。 这种设计建议引起了经验丰富的开发人员的关注:«接口一旦发布,它们是不可变的! 这是一个巨大的变化!» C#8.0添加了用于升级接口的默认接口实现 。 库作者可以向界面添加新成员,并为这些成员提供默认实现。

Default interface implementations enable developers to upgrade an interface while still enabling any implementors to override that implementation. Users of the library can accept the default implementation as a non-breaking change. If their business rules are different, they can override.

默认接口实现使开发人员可以升级接口,同时仍然允许任何实现者覆盖该实现。 库的用户可以接受默认实现作为不间断的更改。 如果它们的业务规则不同,则可以覆盖它们。

使用默认界面成员升级 (Upgrade with default interface members)

The team agreed on the most likely default implementation: a loyalty discount for customers.

团队就最有可能的默认实施达成了一致:为客户提供忠诚度折扣。

The upgrade should provide the functionality to set two properties: the number of orders needed to be eligible for the discount, and the percentage of the discount.This makes it a perfect scenario for default interface members. You can add a method to the ICustomer interface, and provide the most likely implementation.All existing, and any new implementations can use the default implementation, or provide their own.

升级应提供设置两个属性的功能:有资格获得折扣的订单数量和折扣的百分比,这使其成为默认界面成员的理想方案。 您可以向ICustomer接口添加方法,并提供最可能的实现。所有现有实现以及任何新实现都可以使用默认实现,也可以提供自己的实现。

First, add the new method to the implementation:

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

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

The library author wrote a first test to check the implementation:

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

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

Notice the following portion of the test:

注意测试的以下部分:

// Check the discount:
ICustomer theCustomer = c;
Console.WriteLine($"Current discount: {theCustomer.ComputeLoyaltyDiscount()}");

That cast from SampleCustomer to ICustomer is necessary. The SampleCustomerclass doesn't need to provide an implementation for ComputeLoyaltyDiscount; that's provided by the ICustomer interface. However, the SampleCustomer class doesn't inherit members from its interfaces. That rule hasn't changed. In order to call any method declared and implemented in the interface, the variable must be the type of the interface, ICustomer in this example.

SampleCustomerICustomer是必要的。 SampleCustomer类不需要为ComputeLoyaltyDiscount提供实现; 由ICustomer界面提供。 但是, SampleCustomer类不会从其接口继承成员。 该规则没有改变。 为了调用在接口中声明和实现的任何方法,该变量必须是接口的类型,在此示例中为ICustomer

提供参数化 (Provide parameterization)

That's a good start. But, the default implementation is too restrictive. Many consumers of this system may choose different thresholds for number of purchases, a different length of membership, or a different percentage discount.You can provide a better upgrade experience for more customers by providing a way to set those parameters. Let's add a static method that sets those three parameters controlling the default implementation:

这是一个好的开始。 但是,默认实现过于严格。 该系统的许多消费者可能会选择不同的购买门槛,不同的会员资格长度或不同的折扣百分比。您可以通过提供一种设置这些参数的方法来为更多客户提供更好的升级体验。 让我们添加一个静态方法,该方法设置这三个参数来控制默认实现:

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

There's many new language capabilities shown in that small code fragment.Interfaces can now include static members, including fields and methods.Different access modifiers are also enabled. The additional fields are private, the new method is public. Any of the modifiers are allowed on interface members.

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

Applications that use the general formula for computing the loyalty discount, but different parameters, don't need to provide a custom implementation; they can set the arguments through a static method. For example, the following code sets a «customer appreciation» that rewards any customer with more than one month's membership:

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

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

扩展默认实现 (Extend the default implementation)

The code you've added so far has provided a convenient implementation for those scenarios where users want something like the default implementation, or to provide an unrelated set of rules. For a final feature, let's refactor the code a bit to enable scenarios where users may want to build on the default implementation.

到目前为止,您添加的代码为用户需要默认实现之类的场景或提供一组不相关的规则的场景提供了便捷的实现。 对于最终功能,让我们对代码进行一些重构,以启用用户可能希望基于默认实现的方案。

Consider a startup that wants to attract new customers. They offer a 50% discount off a new customer's first order. Otherwise, existing customers get the standard discount. The library author needs to move the default implementation into a protected static method so that any class implementing this interface can reuse the code in their implementation. The default implementation of the interface member calls this shared method as well:

考虑一家想吸引新客户的创业公司。 他们为新客户的第一笔订单提供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;
}

In an implementation of a class that implements this interface, the override can call the static helper method, and extend that logic to provide the «new customer» discount:

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

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

You can see the entire finished code in our [samples repo on GitHub](You can get the starter application on our samples repo on GitHub.

您可以在[GitHub上的示例存储库]中查看完整的完整代码(您可以在GitHub上的示例存储库中获取starter应用程序。

These new features mean that interfaces can be updated safely when there's a reasonable default implementation for those new members. Carefully design interfaces to express single functional ideas that can be implemented by multiple classes. That makes it easier to upgrade those interface definitions when new requirements are discovered for that same functional idea.

这些新功能意味着,对于那些新成员有合理的默认实现时,可以安全地更新接口。 精心设计接口以表达可以由多个类实现的单个功能思想。 当发现相同功能思想的新需求时,这将使升级这些接口定义变得更加容易。

翻译自: https://habr.com/en/company/microsoft/blog/456238/

.c# 调用接口

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值