调用接口方法如何在C#中自动调用派生类方法?

时间:2017-06-26 17:29:16

标签: c# dependency-injection interface

鉴于以下关于依赖倒置的复数的例子,我对这是如何工作非常困惑。所以我们给出了以下接口和派生类实现。

public interface INotifyCustomer
{
    void NotifyCustomer(Cart cart);
}

public class NotifyCustomerService : INotifyCustomer
{
    public void NotifyCustomer(Cart cart)
    {
        string customerEmail = cart.CustomerEmail;
        if (!String.IsNullOrEmpty(customerEmail))
        {
            using (var message = new MailMessage("orders@somewhere.com", customerEmail))
            using (var client = new SmtpClient("localhost"))
            {
                message.Subject = "Your order placed on " + DateTime.Now;
                message.Body = "Your order details: \n " + cart;

                try
                {
                    client.Send(message);
                }
                catch (Exception ex)
                {
                    Logger.Error("Problem sending notification email", ex);
                    throw;
                }
            }
        }
    }
}

我无法理解的部分是这样的想法:简单地调用如下所示的接口以某种方式调用派生类而不实例化甚至提及派生类。示例如下:

public class Order
{

    private readonly Cart cart;
    private readonly PaymentDetails paymentDetails;
    private readonly INotifyCustomer notifyCustomer;

    public Order(Cart cart, PaymentDetails paymentDetails, INotifyCustomer notifyCustomer)
    {
        this.cart = cart;
        this.paymentDetails = paymentDetails;
        this.notifyCustomer = notifyCustomer;
    }
    public void Checkout(Cart cart, PaymentDetails paymentDetails, bool notify)
    {
        if (paymentDetails.PaymentMethod == PaymentMethod.CreditCard)
        {
            ChargeCard(paymentDetails, cart);
        }

        ReserveInventory(cart);

        if (notify)
        {
            notifyCustomer.NotifyCustomer(cart);
        }
    }
}

任何人都可以帮我解释一下吗?我并不是C#的新手,但是并没有使用太长时间,我在过去几年里更像是一个java人,我知道C#和Java之间存在根本区别。但我看不出这是如何或为何有效?

3 个答案:

答案 0 :(得分:3)

即使这个问题确实没有明确的“答案”(可能过于广泛的SO范围),我仍然希望发布一个答案,希望有助于巩固依赖注入的理念。

考虑一下:

从现在开始,您编写的每一段代码都只会引用对象的摘要。摘要意味着接口(或可能是抽象类或基类,但现在我的观点之外)。

这样做的好处是你的代码不关心抽象是如何实现的,它所知道或关心的是它所期望的那些方法或属性。

这为您带来了各种好处:

  • 您的代码变得更加模块化,因为您可以通过注入不同的依赖项轻松地交换具体实现。
  • 你可以让“模拟”类与实际类一致(想想你需要担心HTTP连接的API服务,现在你可以通过在模拟服务中伪造HTTP请求来对其他代码进行单元测试)。
  • Etc等

我认为让你困惑的部分是你所认为的依赖注入的“魔力”。构造函数的参数类型为INotifyCustomer。当你的Order类被实例化时,那些参数被提供(是的,我知道可以在技术上传递null)。既然我们已经确定你的代码绝对无关紧要传入的内容,只要它的类型为INotifyCustomer,你的代码就会像直接传入具体内容一样运行(但是现在我们所有依赖注入的好处。)

答案 1 :(得分:1)

您的界面实施是合同。它简单地说,“如果你实现我(接口),那么你必须提供这个方法NotifyCustomer,它接受​​一个Cart并且不返回一个值(即void)。如果您实施它,只要您执行上述操作,就可以在NotifyCustomer方法中执行任何操作。

您的班级Order不知道notifyCustomer是什么,只是它实现了INotifyCustomer。但是,您的编译器知道,因此当代码编译并运行时,它实际上会在NotifyCustomerService中调用您的代码。通过利用依赖注入,您可以设计代码而无需关心或知道依赖关系是如何实现的,只要它们满足合同即可。

答案 2 :(得分:1)

实际上,类Order中的任何内容都没有实例化NotifyCustomerService,但它知道INotifyCustomer类型的对象具有NotifyCustomer方法,该方法具有指定的参数和返回类型。它不必知道如何在类NotifyCustomerService中实现该方法。