A partir do C # 8.0 no .NET Core 3.0, ao criar um membro de uma interface, você pode determinar sua implementação. O cenário mais comum é adicionar membros com segurança a uma interface já lançada e usada por inúmeros clientes.
Neste guia, você aprenderá como:
- É seguro estender interfaces adicionando métodos com implementações.
- Crie implementações parametrizadas para maior flexibilidade.
- Obtenha o direito de implementar implementações mais específicas com a possibilidade de controle manual.

Por onde começar?
Primeiro, você precisa configurar a máquina para funcionar com o .NET Core, incluindo o compilador da visualização C # 8.0. Esse compilador está disponível a partir do Visual Studio 2019 ou do SDK de visualização do .NET Core 3.0 mais recente. Os membros da interface padrão estão disponíveis a partir do .NET Core 3.0 (Visualização 4).
Visão geral do cenário
Este tutorial começa com a primeira versão da biblioteca de relacionamento com o cliente. Você pode obter o aplicativo inicial em nosso repositório no GitHub . A empresa que criou essa biblioteca supôs que os clientes com aplicativos existentes os adaptariam a essa biblioteca. Os usuários foram apresentados com as definições mínimas de interface para implementação:
public interface ICustomer { IEnumerable<IOrder> PreviousOrders { get; } DateTime DateJoined { get; } DateTime? LastOrder { get; } string Name { get; } IDictionary<DateTime, string> Reminders { get; } }
Também foi definida uma segunda interface que mostra a ordem:
public interface IOrder { DateTime Purchased { get; } decimal Cost { get; } }
Com base nessas interfaces, a equipe pode criar uma biblioteca para seus usuários para criar a melhor experiência para os clientes. O objetivo da equipe era aumentar o nível de interação com os clientes existentes e desenvolver relacionamentos com os novos.
É hora de atualizar a biblioteca para o próximo lançamento. Um dos recursos mais populares é a adição de descontos a clientes fiéis que fazem um grande número de pedidos. Esse novo desconto individual é aplicado toda vez que um cliente faz um pedido.Com cada implementação do ICustomer, regras diferentes podem ser definidas para um desconto por lealdade.
A maneira mais conveniente de adicionar esse recurso é expandir a interface do ICustomer
com descontos. Esta proposta causou preocupação entre desenvolvedores experientes. “As interfaces são imutáveis após o lançamento! Esta é uma mudança crítica! ” No C # 8.0, foram implementadas implementações de interface padrão para atualização de interfaces. Os autores da biblioteca podem adicionar novos membros e implementá-los por padrão
A implementação padrão de interfaces permite que os desenvolvedores atualizem a interface, enquanto ainda permitem que outros desenvolvedores substituam essa implementação. Os usuários da biblioteca podem aceitar a implementação padrão como uma alteração não crítica.
Atualizar usando membros da interface padrão
A equipe concordou com a implementação padrão mais provável: um desconto na fidelidade do cliente.
A atualização deve ser funcional para definir duas propriedades: o número de pedidos necessários para receber um desconto e a porcentagem do desconto. Isso o torna um script ideal para membros da interface padrão. Você pode adicionar um método à interface do ICustomer e fornecer sua implementação mais provável. Todas as implementações existentes e novas podem ser implementadas por padrão ou ter suas próprias configurações.
Primeiro, adicione um novo método à implementação:
O autor da biblioteca escreveu o primeiro teste para verificar a implementação:
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);
Preste atenção na seguinte parte do teste:
Esta parte, do SampleCustomer
ao ICustomer
é importante. A classe SampleCustomer
não SampleCustomer
fornecer uma implementação para ComputeLoyaltyDiscount
; isso é fornecido pela interface do ICustomer
. No entanto, a classe SampleCustomer
não herda membros de suas interfaces. Esta regra não mudou. Para chamar qualquer método implementado em uma interface, a variável deve ser um tipo de interface, neste exemplo ICustomer
.
Parametrização
Este é um bom começo. Mas a implementação padrão é muito limitada. Muitos consumidores deste sistema podem escolher limites diferentes para o número de compras, diferentes durações de associação ou diferentes descontos em porcentagem.Você pode melhorar o processo de atualização para mais clientes, fornecendo uma maneira de definir esses parâmetros. Vamos adicionar um método estático que define esses três parâmetros que controlam a implementação padrão:
Este pequeno pedaço de código mostra muitos novos recursos de idioma. As interfaces agora podem incluir membros estáticos, incluindo campos e métodos. Vários modificadores de acesso também estão incluídos. Os campos adicionais são privados e o novo método é público. Qualquer um dos modificadores é permitido para membros da interface.
Os aplicativos que usam a fórmula geral para calcular descontos de fidelidade, mas com parâmetros diferentes, não devem fornecer uma implementação personalizada; eles podem definir argumentos pelo método estático. Por exemplo, o código a seguir configura "valorização do cliente", que recompensa qualquer cliente com uma associação de mais de um mês:
ICustomer.SetLoyaltyThresholds(new TimeSpan(30, 0, 0, 0), 1, 0.25m); Console.WriteLine($"Current discount: {theCustomer.ComputeLoyaltyDiscount()}");
Estender a implementação padrão
O código que você adicionou anteriormente forneceu uma implementação conveniente para os cenários em que os usuários desejam algo como a implementação padrão ou fornecem um conjunto de regras não relacionadas. Para a versão final, vamos reorganizar um pouco o código para incluir cenários nos quais os usuários podem querer confiar na implementação padrão.
Considere uma startup que deseja atrair novos clientes. Eles oferecem um desconto de 50% no primeiro pedido de um novo cliente. Os clientes existentes recebem um desconto padrão. O autor da biblioteca precisa mover a implementação padrão para o método protected static
, para que qualquer classe que implemente essa interface possa reutilizar o código em sua implementação. A implementação padrão de um membro da interface também chama este método genérico:
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; }
Na implementação da classe que implementa essa interface, você pode chamar manualmente o método auxiliar estático e estender essa lógica para fornecer um desconto ao "novo cliente":
public decimal ComputeLoyaltyDiscount() { if (PreviousOrders.Any() == false) return 0.50m; else return ICustomer.DefaultLoyaltyDiscount(this); }
Você pode ver todo o código finalizado em nosso repositório no GitHub .
Esses novos recursos significam que as interfaces podem ser atualizadas com segurança se houver uma implementação padrão aceitável para novos membros. Projete interfaces com cuidado para expressar idéias funcionais individuais que podem ser implementadas por várias classes. Isso facilita a atualização dessas definições de interface quando novos requisitos são encontrados para a mesma ideia funcional.