接口的意义C#

时间:2010-02-08 10:47:52

标签: c# interface

我想知道Interface的重要用途。我已经阅读了很多文章,但没有清楚地了解界面的概念。

我写了一个小程序。我已经定义了接口Itest.Class(Manager)已实现Interface.Another class(Employee)尚未实现的接口。但是在类(DoSomething())的接口中定义了相同的方法(Employee)。我可以从类对象中调用该方法。那我为什么要去实现接口呢?我可以直接在类中实现该方法并调用该方法。为什么我要在接口中执行额外的步骤,然后按类继承接口。我知道接口支持多重继承,但在这个例子中我没有使用多重继承。

感谢您的任何想法或意见。

public interface Itest
{
    void DoSomething();
}

public class Manager:Itest
{
    public void DoSomething()
    {
        Console.WriteLine("test....");
    }

}
class Employee
{
    public void DoSomething()
    {
        Console.WriteLine("test....");
    }
}

class Program
{
    static void Main(string[] args)
    {
        Manager m = new Manager();
        m.DoSomething();
        Employee e = new Employee();
        e.DoSomething();
        Console.ReadLine();
    }
}

12 个答案:

答案 0 :(得分:15)

接口允许您使用多重继承进行排序。在您的示例中,它允许您将Employee或Manager的实例放入同一个变量中,然后对该变量调用DoSomething,并将方法调用分派给该变量当前引用的实例。例如:

public interface IEmployee {
    void DoSomething();
}
// assume Manager and Employee both implement IEmployee

IEmployee ie = new Manager();
ie.DoSomething();    // calls Manager.DoSomething()
ie = new Employee();
ie.DoSomething();    // calls Employee.DoSomething()

如果您没有使用接口,则必须执行以下操作:

object o;

// assign Manager or Employee (or something else!) to o

if (o is Manager)
    ((Manager)o).DoSomething();
else if (o is Employee)
    ((Employee)o).DoSomething();

接口定义契约,只要实例实现该接口,您就不关心它在运行时的实际情况。您可以让同一个类实现多个接口,然后在这些接口的所有变量中使用该类的实例。你不能对抽象类使用相同的东西,因为一个类一次只能继承一个类。

我现在使用接口的一个例子是定义一个对象模型 - 我有各种属性的接口(IHasStorageIHasPrivilegesIHasCheezburger),然后是类表示具体对象实现了哪个接口适用于该类的属性

答案 1 :(得分:10)

接口用于抽象(抽象类也用于此,但通常包含一些旨在重用的实现)。

在C#中,它们允许使用多重继承,这意味着您可以在一个类中实现许多不同的接口。

如果您有许多不同的接口实现,只要您使用接口声明,就可以互相替换它们。

例如:

IAnimal可由CatDog实施。在其他代码中,您希望调用接口中声明的Talk方法。您的代码无需关心它是Cat对象还是Dog对象。您可以添加DuckHuman,而不是更改该段代码。

在使用模拟对象测试代码时,这也很有用,因此可以用一个简单的对象代替真实的对象进行测试。

一些接口用作标记,因此反射可以轻松地拾取它们(例如,ISerializable接口,将类标记为可序列化。)

答案 2 :(得分:4)

实现继承模型是“IS A KIND OF”关系,而接口继承模型是“CAN BEHAVE LIKE”关系。许多BCL界面名称以“-able”结尾并代表能够做某事的能力并非巧合。要演示,请对以下代码进行成像:

class Person
{
  public string Name{get;set;}
  public void Walk() {/*code elided*/}
}

class Employee : Person
{
  public int Id{get;private set;}
}

显然,员工“是一种”人。所有员工都是人,因此他们都有一个名字,可以走路()。无论如何使Person或Employee成为抽象的,它并没有改变所有员工都是人的事实。

现在让我们更抽象一点,谈谈作为一种载体的概念。作为车辆的绝对必要条件是它必须能够移动和停止。你可能包括转向和载客,但我保持非常抽象。

让我们考虑一些作为车辆的东西。当然是一辆车,但一个人怎么样?他们可以移动和停止(当我给我的侄子和侄女背驮我也带着乘客!)一把小便椅?我永远在办公室里徘徊。我也是一名敏锐的水手,并利用风来加速和减速我的车辆。

你无法使用实现继承对这种关系进行建模,因为你有很多不同的东西,“可以像”一样,但不必从同一个基类继承。

pogostick(向专业的pogo-ers道歉)是一种玩具,但可以作为车辆使用。并非所有玩具都是车辆。一个人与汽车没有任何关系,除了它可以作为一辆汽车。

interface IVehicle
{
  void Move();
  void Stop();
}

class Toy{}

class PogoStick : Toy, IVehicle
{
  public void Move(){ /* boing boing */}
  public void Stop(){ /* fall over */}
}

class Car: IVehicle
{
  public void Move(){ /* vroom vroom */}
  public void Stop(){ /* <screeeech!> */}
}

class Person : IVehicle
{
  public string Name{get;set;}
  public void Walk() {/*code elided*/}
  void IVehicle.Move() { Walk(); }
  void IVehicle.Stop() { /*whatever!*/}
}

class Program
{
  static void Main()
  {
    IVehicle[] vehicles = new IVehicle[3];
    vehicles[0] = new PogoStick();
    vehicles[1] = new Car();
    vehicles[2] = new Employee(); //implements IVehicle because it IS A KIND OF Person

    vehicles.ForEach(v => v.Move());

    //it's worth pointing out that
    vehicles[2].Stop();
    //works fine, but
    Person p = new Person();
    p.Move();
    //won't, as I explicitly implemented the interface, meaning I can only get at the
    //methods via a reference to the interface, not to the implementing class.
  }
}

要使用.NET本身的示例,地球上的字符串与List有什么共同之处?不是很多,除了我可以“预先”他们两个:

class Demo
{
  static void Main()
  {
    string s = "Hello!";
    List<Employee> payroll = new List<Employee>();

    for each (var item in s)
    {
      Console.WriteLine(item);
    }

    for each (var item in payroll)
    {
      Console.WriteLine(item);
    }
}

string和List的公共基类是object,但并非所有对象都是“for-each-able”,因此必须要有其他内容。也就是说,它们都实现了IEnumerable接口(那就是-able!)

答案 3 :(得分:2)

当您将接口添加到Employee时,这两个类通过该接口变得兼容:

public class Manager:Itest { ... }

class Employee:Itest { ... }

static void ShowSomething(Itest test)
{
   test.DoSomething();
}

static void Main(string[] args)
{

    Manager m = new Manager();            
    Employee e = new Employee();

    ShowSomething(m);
    ShowSomething(e);
}

看看BCL如何使用界面,开始查找IDisposable,ISerializable,IComprable和IEnumerable

答案 4 :(得分:1)

接口的继承使您可以在不破坏代码的情况下使用其他类或对象更改实现。接口意味着您与代码的客户签订合同,您将提供一些功能,并且他们不必知道要为此调用哪个特定类。 接口添加了一个抽象层,因此您的代码客户端将不依赖于您实现解决方案的方式。他们只知道你会提供他们,界面里有什么。

答案 5 :(得分:1)

提供了一个非常好的接口类比Matthew Cochran

“这使得导航更容易”代码世界“。想象一下,如果不是学习驾驶汽车然后能够开车,我们必须学习如何驾驶每辆车的每个实例我们进入。如果在学习如何驾驶福特平托后我们必须重新开始才能找到野马,这将是非常低效的。一种更有效的方法是处理汽车界面:方向盘,转向信号,油门踏板和制动器。这样,无论接口后端实现什么,我们都不在乎,因为最终它订阅了基本的汽车合同,这就是我们如何处理它(通过界面).2

除了上面的一般解释,大多数现代软件设计模式,非常依赖于依赖注入等接口

考虑这个例子:

你有一个能够播放媒体文件(mp3)的课程。您将该课程提供给尝试播放MPEG类型文件的朋友。如果不对你的班级做出重大改变,他就不可能这样做。

public class MusicPlayer 
    {
       void Play(Mp3 _mp3File){}
    }

考虑这个

而不是将mp3文件的类型传递给Play Method,如果传递此方法,则从类型MediaType的接口派生。

    public interface MediaType { }

    public class Mp3 : MediaType
    { }

    public class MPEG : MediaType
    { }

和班级:

    public class MusicPlayer 
    {
       void Play(MediaType _mediaFile){}
    }

在这种情况下,您可以派生另一种MediaFile类型,并从MediaType中获取类似MPEG并将其传递给Play方法,它会很乐意接受它并为您播放(提供逻辑)。

   public class TestPlayers
    {
        public void PlayMedia()
        {
            MusicPlayer musicPlayer = new MusicPlayer();
            musicPlayer.Play(new Mp3());
            musicPlayer.Play(new MPEG());
        }       
    }

希望这有帮助

答案 6 :(得分:1)

接口在更复杂的场景中很有用,例如

(1)您需要多重继承(在c#中,您不能从2个类继承),例如你有接口IProduct,IDisposable。并非所有产品都需要处理,因此在所有产品上实施它是没有意义的。

(2)当您使用依赖注入(控制反转)和模拟框架(例如RhinoMocks)进行单元测试时 - 那么您必须使用接口,否则您的模拟框架将无法工作。

答案 7 :(得分:1)

你知道,有趣的是,我今天正在和我们的一位开发人员谈论这个话题。

以下示例是解释界面及其可用性的最简单方法。

interface IGraphicsRenderer
{
     Render(List<Triangle> triangles);
}

然后你可能有两种类型的渲染引擎,Direct3D或OpenGL

class Direct3DRenderer : IGraphicsRenderer
{
   public void Render(List<Triangle> triangles);
}

class OpenGLRenderer: IGraphicsRenderer
{
   public void Render(List<Triangle> triangles);
}

为了表明它的用处,你可能会有类似

的内容
IGraphicsRenderer renderer = new Direct3DRenderer();
renderer.Render(foo);

要更改渲染器,您只需更改初始化。

IGraphicsRenderer renderer = new OpenGLRenderer();

答案 8 :(得分:1)

当你有太多类来自Interface类的驱动时,你要确保它们都实现了一个方法。

然后,如果您的界面发生变化(例如:

       public Interface IAnimal
            {
                public void Talk();
                public void Eat();
            }

然后添加另一种方法

       public Interface IAnimal
            {
                public void Talk();
                public void Sleep();
                public void Eat();
            }

然后你可以确保所有这些都将实现Sleep()方法。如果你有太多来自IAnimal Inteface的类,那么你必须为所有这些类实现Sleep()。这有助于您尽可能简单地扩展派生类

答案 9 :(得分:0)

当然,您可以在不实现接口的情况下实现接口中声明的方法。简单的put,接口只是确保你不要忘记它(如果没有实现任何接口成员,它们将给你编译错误。)

答案 10 :(得分:0)

我认为你想要一个非常简单的答案。

有两种类型的继承:

  • TYPE 1:接口继承(in 简单的术语:当外面的 class是继承的)
  • TYPE 2: 实现继承(当 在类的内部继承)

如果你写

class MyClass : Base {}

您正在使用BOTH类型1和2.但是如果您实现的是清除类型1的接口。

类型1用于多态用途,类型2用于代码重用。

所以最重要的是如果你想使用多态而你

  • 不想提供任何 实施
  • 或根本做不到。主要是在多重继承的情况下 - 注意:c ++允许它,它遵循另一种哲学

接口适合您:)

还有另一种用途(例如强制执行方法)但在我看来这是重点。

答案 11 :(得分:0)

接口适用于设计。实际实现遵循设计中定义的内容(读取接口)。接口的概念使人们可以灵活地设计系统,而无需深入了解实现的细节,或者通过在将来实现接口来保持设计的可扩展性。这也称为抽象。

虽然界面在设计时提供了灵活性,但它在实施时设置了约束。接口的任何实现都需要完成,这意味着需要在实现时实现接口中定义的所有规范。此时的另一个灵活性是可以在一个类中实现多个接口。

这是一个非常强大的概念,在设计时提供了极高的灵活性,并确保实施不会破坏设计。