正确使用Interface?

时间:2014-02-09 17:13:36

标签: c# interface

我读了一些关于接口的文章,但对我来说还不够清楚。请帮助找到使用接口的正确方法。我的问题在codeample的评论中:

using System;
namespace IfTest
{
    public interface ICalculator
    {
        void Sum(int a, int b);
    }

    public class MyCalc : ICalculator
    {
        public void Sum(int a, int b)
        {
            Console.WriteLine(a + b);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            //What's the difference between
            ICalculator mycalc;
            mycalc = new MyCalc();
            mycalc.Sum(5, 5);

            //and this. When should we use this way?
            MyCalc mc = new MyCalc();
            mc.Sum(5, 5);
        }
    }
}

4 个答案:

答案 0 :(得分:5)

使用ICalculator mycalc声明类型为ICalculator的变量,在编译时,您只能调用属于此接口的方法。您将无法调用不属于接口但仅属于实现类的方法。

使用MyCalc mc声明类型为MyCalc的变量,在编译时,您只能调用此类及其继承的接口上的所有方法。

在运行时,两者之间根本没有区别。

对接口进行编程时,建议使用对象层次结构中最抽象的可能类型。所以在这种情况下,这将是ICalculator接口。

这样可以更好地区分调用代码和实际实现。如果调用代码是针对接口编程的,则它不再依赖于可以与其他实现交换的特定MyCalc实现。

答案 1 :(得分:1)

使用Dependency injection时接口完全生效。假设您有一个使用计算器进行计算的方法。而不是在此方法中实例化计算器,将其作为参数传递给计算器:

 public void PerformSomeCalculations(ICalculator calculator)
 {
     calculator.Sum(5, 5);
     ...
}

这允许您随时通过其他实现更改计算器。方法PerformSomeCalculations对具体实现一无所知。出于测试目的,您甚至可以传递一个虚拟计算器来跟踪已调用的方法,以查看方法PerformSomeCalculations是否完成预期的操作。

你甚至可以提供一个例如Modular arithmetic的计算器,并比较两个非常不同的计算器的行为。

编程与接口使您的代码比编程与特定类型更灵活。编程与接口使Unit testing更容易。

答案 2 :(得分:0)

当您希望程序依赖于抽象合同而不是硬实现时,可以使用接口。通过使用ICalculator接口来定义变量(或字段),您允许程序的未来实现提供该合同的其他实现,

ICalculator mycalc;
mycalc = new MyCalc(); // or mycalc = new OtherCalc(); or mycalc = new FinancialCalc();

将来,当更高效的实现出现时,您可以快速将mycalc替换为实现该接口的其他类,并且您的程序将继续按预期运行。如果您不希望程序因未知的副作用或由MyCalc等硬实现提供的实现细节而失败,这一点尤为重要。

答案 3 :(得分:0)

您通常会遇到一个类使用其他类的服务的情况。 “使用服务”意味着调用其公共方法。例如,CalculatorUser使用MyCalc - CalculatorUser实例将在某处调用MyCalc.Sum()

现在想象一下,您向客户提供包含类CalculatorUserMyCalc的应用程序。你可以这样写:

public class CalculatorUser
{
    private MyCalc _myCalc;

    public CalculatorUser(MyCalc myCalc)
    {
        _myCalc = myCalc;
    }

    public void PerformAddition(int a, int b)
    {
        _myCalc.Sum(a, b);
    }
}

class Program
{
    static void Main(string[] args)
    {
        MyCalc calculator = MyCalc();
        CalculatorUser calcUser = new CalculatorUser(calculator);
        calcUser.PerformAddition(1, 2);

        Console.ReadKey();
    }
}

一切看起来都不错,但过了一段时间后,客户又回到了新的要求:“我希望CalculatorUser有更多的选择:我希望它能够在旧的简单计算器{{}之间进行选择1}}和一个新的,花哨的计算器,显示操作数,操作和结果!此外,这个选择必须在运行时进行。“

您意识到现在必须创建MyCalc并更改MyFancyCalc才能支持此新要求。您可能希望将另一个CalculatorUser类型的成员添加到MyFancyCalc,然后将另一个方法CalculatorUser添加到PerformAdditionWithFancyCalc()。但是,如果您的客户需要10种其他类型的计算器,您会为每种计算器添加新成员和方法吗?如果您使用户和服务提供商保持紧密耦合,那么每个需求的变化都会导致用户不断变化,解决方案就是让用户不了解特定的服务提供商,而只了解它提供的服务:这些服务的名称是什么,什么是输入值的类型,它们的输出类型是什么?这实际上是使服务提供商的公共接口的原因。 MyFancyCalc无需知道计算器的具体实现 - CalculatorUserMyCalc,所有它必须知道的是它使用的任何计算器都有一个接受两个的方法MyFancyCalc Sum值并返回int。通过这种方式,您可以将用户与特定计算器分离,并使其能够使用任何以接口中描述的方式实现void的计算器。如果您创建Sum课程,则无需更改MyExtraFancyCalc

因此,为了满足新的要求(在运行时选择计算器),您可以编写如下内容:

CalculatorUser

用户被问到“你想用花式计算器吗?(y / n)”如果类型为“n”,则使用旧计算器,输出只是“3”,但如果答案是“y”,花式计算器使用并输出“1 + 2 = 3”

此示例显示了接口的功能(基本上是一个简单的模式称为依赖注入)。

在现实生活中,您经常会遇到这样的情况:您的客户拥有一个消费者(public interface ICalculator { void Sum(int a, int b); } public class MyCalc : ICalculator { public void Sum(int a, int b) { Console.WriteLine(a + b); } } public class MyFancyCalc : ICalculator { public void Sum(int a, int b) { Console.WriteLine("{0} + {1} = {2}", a, b, a + b); } } public class CalculatorUser { private ICalculator _calculator; public CalculatorUser(ICalculator calculator) { _calculator = calculator; } public void PerformAddition(int a, int b) { _calculator.Sum(a, b); } } class Program { static void Main(string[] args) { bool useFancyCalculator = GetUseFancyCalculator(); ICalculator calculator = CreateCalculator(useFancyCalculator); CalculatorUser calcUser = new CalculatorUser(calculator); calcUser.PerformAddition(1, 2); Console.ReadKey(); } static bool GetUseFancyCalculator() { Console.WriteLine("Would you like to use fancy calculator? (y/n)"); string choice = Console.ReadLine(); return (choice == "y"); } static ICalculator CreateCalculator(bool createFancyCalculator) { ICalculator calculator = null; if (createFancyCalculator) calculator = new MyFancyCalc(); else calculator = new MyCalc(); return calculator; } } )的应用程序,该应用程序从不或很少更改和插件(DLL)包含服务提供商的各种实现。主应用程序在运行时检测哪些插件可用并选择一个,具体取决于运行时生成的用户选择(或其他一些标准)。