我真的不明白接口存在的原因。据我所知,这是c#中不存在的多继承的一种工作(至少我是这么被告知的)。
我所看到的是,您预定义了一些成员和函数,然后必须在类中再次重新定义它们。从而使接口成为冗余。它只是感觉像句法……嗯,垃圾对我来说(请没有冒犯的意思。Junk是指无用的东西)。
在下面的例子中,我将创建一个名为Pizza的基类,而不是一个接口。
简单示例(取自不同的堆栈溢出贡献)
public interface IPizza
{
public void Order();
}
public class PepperoniPizza : IPizza
{
public void Order()
{
//Order Pepperoni pizza
}
}
public class HawaiiPizza : IPizza
{
public void Order()
{
//Order HawaiiPizza
}
}
class Program {
static void Main(string[] args) {
IMachine machine = new Machine();
machine.Run();
Console.ReadKey();
}
}
class Machine : IMachine {
private void Run() {
Console.WriteLine("Running...");
}
void IMachine.Run() => Run();
}
interface IMachine
{
void Run();
}
让我从另一个角度来描述一下。让我们根据上面的例子来创建一个故事;
程序、机器和IMachine是我们故事的演员。程序想要运行,但它没有这个能力,而机器知道如何运行。机器和IMachine是最好的朋友,但程序和机器没有说话的关系。于是程序和IMachine做了一个交易,决定IMachine通过观察机器(就像一个反射器)来告诉程序如何运行。
Program在IMachine的帮助下学习如何运行。
接口提供通信和开发松散耦合的项目。
PS:我把具体类的方法作为私有。我在这里的目标是通过防止访问具体的类属性和方法来实现松耦合,只允许通过接口访问它们。(所以我明确地定义了接口的方法)。
如果我正在使用一个API来绘制形状,我可能想使用DirectX或图形调用,或OpenGL。因此,我将创建一个接口,它将从您调用的内容中抽象出我的实现。
所以你调用一个工厂方法:MyInterface i = MyGraphics.getInstance()。然后,你有一个契约,所以你知道你可以在MyInterface中期望什么功能。你可以调用i。drawrectangle或i。drawcube并且知道如果你把一个库换成另一个库,函数是被支持的。
如果您正在使用依赖注入,这就变得更加重要,因为您可以在XML文件中交换实现。
所以,你可能有一个加密库可以导出,供一般使用,而另一个加密库只出售给美国公司,区别在于你改变了配置文件,而程序的其余部分不会改变。
这在。net中的集合中被大量使用,就像你应该只使用,例如,列表变量,不要担心它是一个数组列表还是LinkedList。
只要您编写了接口代码,那么开发人员就可以更改实际的实现,而程序的其余部分则保持不变。
这在单元测试时也很有用,因为您可以模拟出整个接口,因此,我不需要访问数据库,而是只返回静态数据的模拟出来的实现,因此我可以测试我的方法,而不用担心数据库是否需要维护。
上面的例子没有多大意义。你可以使用类来完成上面所有的例子(如果你想让它只表现为一个契约,那么它就是抽象类):
public abstract class Food {
public abstract void Prepare();
}
public class Pizza : Food {
public override void Prepare() { /* Prepare pizza */ }
}
public class Burger : Food {
public override void Prepare() { /* Prepare Burger */ }
}
你会得到和界面相同的行为。您可以创建一个List<Food>,并迭代w/o知道什么类位于顶部。
更合适的例子是多重继承:
public abstract class MenuItem {
public string Name { get; set; }
public abstract void BringToTable();
}
// Notice Soda only inherits from MenuItem
public class Soda : MenuItem {
public override void BringToTable() { /* Bring soda to table */ }
}
// All food needs to be cooked (real food) so we add this
// feature to all food menu items
public interface IFood {
void Cook();
}
public class Pizza : MenuItem, IFood {
public override void BringToTable() { /* Bring pizza to table */ }
public void Cook() { /* Cook Pizza */ }
}
public class Burger : MenuItem, IFood {
public override void BringToTable() { /* Bring burger to table */ }
public void Cook() { /* Cook Burger */ }
}
然后你可以把它们都作为菜单项使用,而不用关心它们如何处理每个方法调用。
public class Waiter {
public void TakeOrder(IEnumerable<MenuItem> order)
{
// Cook first
// (all except soda because soda is not IFood)
foreach (var food in order.OfType<IFood>())
food.Cook();
// Bring them all to the table
// (everything, including soda, pizza and burger because they're all menu items)
foreach (var menuItem in order)
menuItem.BringToTable();
}
}
考虑接口的最简单方法是认识继承的意义。如果类CC继承了类C,这意味着:
类CC可以使用类C的任何public或protected成员,就像它们是自己的一样,因此只需要实现父类中不存在的东西。
对CC的引用可以传递或分配给期望对C的引用的例程或变量。
遗传的这两个功能在某种意义上是相互独立的;虽然继承同时应用这两个,但也可以应用第二个而不应用第一个。这很有用,因为允许一个对象从两个或多个不相关的类继承成员要比允许一种类型可以替代多种类型复杂得多。
接口有点像抽象基类,但有一个关键的区别:继承基类的对象不能继承任何其他类。相反,一个对象可以实现一个接口,而不影响它继承任何所需类或实现任何其他接口的能力。
One nice feature of this (underutilized in the .net framework, IMHO) is that they make it possible to indicate declaratively the things an object can do. Some objects, for example, will want data-source object from which they can retrieve things by index (as is possible with a List), but they won't need to store anything there. Other routines will need a data-depository object where they can store things not by index (as with Collection.Add), but they won't need to read anything back. Some data types will allow access by index, but won't allow writing; others will allow writing, but won't allow access by index. Some, of course, will allow both.
If ReadableByIndex and Appendable were unrelated base classes, it would be impossible to define a type which could be passed both to things expecting a ReadableByIndex and things expecting an Appendable. One could try to mitigate this by having ReadableByIndex or Appendable derive from the other; the derived class would have to make available public members for both purposes, but warn that some public members might not actually work. Some of Microsoft's classes and interfaces do that, but that's rather icky. A cleaner approach is to have interfaces for the different purposes, and then have objects implement interfaces for the things they can actually do. If one had an interface IReadableByIndex and another interface IAppendable, classes which could do one or the other could implement the appropriate interfaces for the things they can do.