C# 中的接口(初学者指南)






4.60/5 (379投票s)
2007年5月10日
8分钟阅读

1616107
C# 接口教程
引言
C# 中的接口提供了一种实现运行时多态性的方法。使用接口,我们可以通过相同的接口引用调用不同类中的函数,而使用虚函数,我们可以通过相同的引用调用相同继承层次结构中不同类中的函数。在事情变得复杂之前,我将使用简单明了的示例来解释接口的概念。这是一个简短的示例,展示了接口的外观。
P1.cs
class Demo
{
public static void Main()
{
System.Console.WriteLine("Hello Interfaces");
}
}
interface abc
{
}
输出
Hello Interfaces
上面的程序可以成功编译并运行,产生预期的输出。上面的程序包含一个名为 Demo
的类,其中包含一个入口点函数 Main()
,它打印 "Hello Interfaces"。上面的程序还定义了一个名为 abc
的接口。接口 abc
目前是空的。让我们向这个接口添加一些元素。
P2.cs
class Demo
{
public static void Main()
{
System.Console.WriteLine("Hello Interfaces");
}
}
interface abc
{
int x;
}
输出
P2.cs(11,3): error CS0525: Interfaces cannot contain fields
错误!C# 中的接口不能包含字段,即变量。上面的程序在接口 abc
中声明了一个名为 x
的整数变量。这就是 C# 编译器无法处理的地方。
P3.cs
class Demo
{
public static void Main()
{
System.Console.WriteLine("Hello Interfaces");
}
}
interface abc
{
void xyz()
{
System.Console.WriteLine("In xyz");
}
}
输出
P3.cs(11,8): error CS0531: 'abc.xyz()': interface members cannot have a
definition
这次我们尝试在接口中包含一个名为 xyz()
的函数,发现这同样会惹恼 C# 编译器。它响亮地告诉我们接口成员不能有定义。这是否意味着如果我们只在接口 abc
中有一个函数声明,C# 编译器就能接受呢?让我们来找出答案。
P4.cs
class Demo
{
public static void Main()
{
System.Console.WriteLine("Hello Interfaces");
}
}
interface abc
{
void xyz();
}
输出
Hello Interfaces
上面的程序可以成功编译并运行,产生预期的输出。最后我们让编译器满意了。C# 中的接口只能包含函数声明。现在让我们看看接口的实际应用。
接口是类以自己的方式实现的契约。这意味着接口将包含函数原型,而实现了该接口的类将必须负责定义由实现的接口声明了原型的函数。
所以是时候让我们的“新郎”类 Demo
和“新娘”接口 abc
进行“婚礼”了。
P4.cs
class Demo : abc
{
public static void Main()
{
System.Console.WriteLine("Hello Interfaces");
}
}
interface abc
{
void xyz();
}
输出
P4.cs(1,7): error CS0535: 'Demo' does not implement interface member
'abc.xyz()'
P4.cs(11,8): (Location of symbol related to previous error)
嗯,在上面的程序中,类 Demo
通过 class
这行实现了接口 demo
: abc
abc
,但一如既往,新婚夫妇之间存在一些小误会。类 Demo
需要负责定义由实现的接口 abc
声明了原型的函数。由于上面的程序中类 Demo
没有实现(即定义)由实现的接口 abc
声明了原型的函数 xyz
,我们遇到了一个错误。要解决此问题,类 Demo
必须负责定义由实现的接口 abc
声明了原型的函数 xyz
。这正是您在以下程序中看到的内容。
P5.cs
class Demo : abc
{
public static void Main()
{
System.Console.WriteLine("Hello Interfaces");
}
void xyz()
{
System.Console.WriteLine("In xyz");
}
}
interface abc
{
void xyz();
}
输出
a.cs(1,7): error CS0536: 'Demo' does not implement interface member
'abc.xyz()'.'Demo.xyz()' is either static, not public,
or has the wrong return type.
a.cs(16,8): (Location of symbol related to previous error)
a.cs(7,8): (Location of symbol related to previous error)
又出错了!类 Demo
实现函数 xyz
并不足够。它必须通过将 xyz
的实现声明为 public
来打动新娘接口 abc
。这正是以下程序所做的。
P6.cs
class Demo : abc
{
public static void Main()
{
System.Console.WriteLine("Hello Interfaces");
xyz();
}
public void xyz()
{
System.Console.WriteLine("In xyz");
}
}
interface abc
{
void xyz();
}
输出
Hello Interfaces
In xyz
对了!上面的程序可以成功编译并运行,产生预期的输出。如前所述,使用接口,我们可以通过相同的接口引用调用不同类中的函数。为此,我们需要有不同的类来实现相同的接口。在上面的程序中,我们的类 Demo
实现了接口 abc
。让我们再创建一个名为 Sample
的类来实现接口 abc
。
P7.cs
class Demo : abc
{
public static void Main()
{
System.Console.WriteLine("Hello Interfaces");
Demo refDemo = new Demo();
refDemo.xyz();
Sample refSample = new Sample();
refSample.xyz();
}
public void xyz()
{
System.Console.WriteLine("In Demo :: xyz");
}
}
interface abc
{
void xyz();
}
class Sample : abc
{
public void xyz()
{
System.Console.WriteLine("In Sample :: xyz");
}
}
输出
In Demo :: xyz
In Sample :: xyz
上面的程序可以成功编译并运行,产生预期的输出。refDemo
是类 Demo
对象的一个引用。refSample
是类 Sample
对象的一个引用。这两个类都实现了接口 abc
,因此定义了它们各自对函数 xyz()
的实现。在入口点函数 Main()
中,通过引用 refDemo
和 refSample
调用了各自类 Demo
和 Sample
的 xyz()
。
现在我们有了两个实现相同接口的不同类,是时候向您展示如何使用相同的接口引用来调用不同类中的函数了。
P8.cs
class Demo : abc
{
public static void Main()
{
System.Console.WriteLine("Hello Interfaces");
abc refabc = new Demo();
refabc.xyz();
abc refabc = new Sample();
refabc.xyz();
}
public void xyz()
{
System.Console.WriteLine("In Demo :: xyz");
}
}
interface abc
{
void xyz();
}
class Sample : abc
{
public void xyz()
{
System.Console.WriteLine("In Sample :: xyz");
}
}
输出
In Demo :: xyz
In Sample :: xyz
上面的程序可以成功编译并运行,产生预期的输出。在 Main()
中,我们有一个类型为接口 abc
的接口引用 refabc
。类 Demo 对象的引用存储在 refabc
中,并通过 refabc
调用了类 Demo
的 xyz()
。接下来,类 Sample 对象的引用存储在 refabc
中,并通过 refabc
调用了类 Sample
的 xyz()
。因此,我们能够通过通用接口引用 refabc
调用属于不同类 Demo
和 Sample
的 xyz()
。
下面的程序使用 for 循环,通过一个与类实现的接口“interface abc”匹配的通用接口引用 refabc
,来调用实现相同接口“interface abc”的类 Demo
和 Sample
的函数。
P9.cs
class Demo : abc
{
public static void Main()
{
abc [] refabc = {new Demo(), new Sample()} ;
for (int i = 0; i<= 1; i++)
refabc[i].xyz();
}
public void xyz()
{
System.Console.WriteLine("In Demo :: xyz");
}
}
interface abc
{
void xyz();
}
class Sample : abc
{
public void xyz()
{
System.Console.WriteLine("In Sample :: xyz");
}
}
输出
In Demo :: xyz
In Sample :: xyz
上面的程序可以成功编译并运行,产生预期的输出。refabc
是一个类型为接口 abc
的数组。它存储了类 Demo
和 Sample
对象的引用。在 for 循环中,我们使用数组 refabc 调用了类 Demo
和 Sample
的函数 xyz()
。一个类可以实现任意数量的接口。请看下面的程序。
P10.cs
class Demo : abc, def
{
public static void Main()
{
System.Console.WriteLine("Hello Interfaces");
abc refabc = new Demo();
refabc.xyz();
}
public void xyz()
{
System.Console.WriteLine("In xyz");
}
public void pqr()
{
System.Console.WriteLine("In xyz");
}
}
interface abc
{
void xyz();
}
interface def
{
void pqr();
}
输出
Hello Interfaces
In xyz
上面的程序可以成功编译并运行,产生预期的输出。类 Demo
实现了接口 abc
,从而实现了函数 xyz()
。类 Demo
还实现了接口 def
,从而实现了函数 pqr()
。类型为 Interface abc
的变量 ref abc
指向类 Demo
的对象。接下来,通过 refabc
调用 Demo
的 xyz()
,因为 refabc
是 Interface abc
类型的变量,它包含了函数 xyz()
的原型。
P11.cs
class Demo : abc, def
{
public static void Main()
{
System.Console.WriteLine("Hello Interfaces");
abc refabc = new Demo();
refabc.xyz();
refabc.pqr();
}
public void xyz()
{
System.Console.WriteLine("In xyz");
}
public void pqr()
{
System.Console.WriteLine("In xyz");
}
}
interface abc
{
void xyz();
}
interface def
{
void pqr();
}
输出
P11.cs(9,5): error CS0117: 'abc' does not contain a definition for 'pqr'
错误!尝试通过 refabc
调用 Demo
的 pqr()
会失败,因为 refabc
是 Interface abc
类型的变量,它包含函数 xyz()
的原型,而不是 pqr()
。可以通过类型为 Interface def
的引用变量来调用 Demo
的 pqr()
,因为接口 def
包含了函数 pqr()
的原型。这正是以下程序所做的。
P12.cs
class Demo : abc, def
{
public static void Main()
{
System.Console.WriteLine("Hello Interfaces");
Demo refDemo = new Demo();
abc refabc = refDemo;
refabc.xyz();
def refdef = refDemo;
refdef.pqr();
}
public void xyz()
{
System.Console.WriteLine("In xyz");
}
public void pqr()
{
System.Console.WriteLine("In pqr");
}
}
interface abc
{
void xyz();
}
interface def
{
void pqr();
}
输出
Hello Interfaces
In xyz
In pqr
上面的程序可以成功编译并运行,产生预期的输出。类 Demo
实现了接口 abc
和 def
。创建了一个类 Demo
的对象,并将其引用存储在 refDemo
中。类型为 Interface abc
的变量 refabc
指向类 Demo
的对象。接下来,通过 refabc
调用 Demo
的 xyz()
,因为 refabc
是 Interface abc
类型的变量,它包含了函数 xyz()
的原型。同样,类型为 Interface def
的变量 refdef
指向类 Demo
的对象。接下来,通过 refdef
调用 Demo
的 pqr()
,因为 refdef
是 Interface def
类型的变量,它包含了函数 pqr()
的原型。
P13.cs
class Demo : abc, def
{
public static void Main()
{
System.Console.WriteLine("Hello Interfaces");
Demo refDemo = new Demo();
abc refabc = refDemo;
refabc.xyz();
def refdef = refDemo;
refdef.xyz();
}
public void xyz()
{
System.Console.WriteLine("In xyz");
}
}
interface abc
{
void xyz();
}
interface def
{
void xyz();
}
输出
Hello Interfaces
In xyz
In xyz
上面的程序可以成功编译并运行,产生预期的输出。接口 abc
和 def
都声明了函数 xyz()
的原型。类 Demo
同时实现了接口 abc
和 def
,并定义了函数 xyz()
。因此,在将类 Demo
的对象引用存储在 refabc
或 refdef
中后,我们可以通过任一接口引用变量(refabc
或 refdef
)调用函数 xyz()
。这就产生了一个问题:我们如何在类 Demo
中拥有一个特定于接口 abc
的 xyz
实现,以及一个特定于 def
的 xyz
实现?嗯,为此我们需要使用如下面程序中的完全限定名。
P14.cs
class Demo : abc, def
{
public static void Main()
{
System.Console.WriteLine("Hello Interfaces");
Demo refDemo = new Demo();
abc refabc = refDemo;
refabc.xyz();
def refdef = refDemo;
refdef.xyz();
}
public void abc.xyz()
{
System.Console.WriteLine("In abc.xyz");
}
public void def.xyz()
{
System.Console.WriteLine("In def.xyz");
}
}
interface abc
{
void xyz();
}
interface def
{
void xyz();
}
输出
a.cs(13,15): error CS0106: The modifier 'public' is not valid for this item
a.cs(18,15): error CS0106: The modifier 'public' is not valid for this item
糟糕!我们使用了完全限定名,结果却出现了错误。这是因为当我们对作为接口一部分的函数使用完全限定名时,编译器不需要像 public
这样的修饰符。所以我们决定从上面的程序中删除访问修饰符 public
。
P15.cs
class Demo : abc, def
{
public static void Main()
{
System.Console.WriteLine("Hello Interfaces");
Demo refDemo = new Demo();
abc refabc = refDemo;
refabc.xyz();
def refdef = refDemo;
refdef.xyz();
}
void abc.xyz()
{
System.Console.WriteLine("In abc.xyz");
}
void def.xyz()
{
System.Console.WriteLine("In def.xyz");
}
}
interface abc
{
void xyz();
}
interface def
{
void xyz();
}
输出
Hello Interfaces
In abc.xyz
In def.xyz
上面的程序可以成功编译并运行,产生预期的输出。完全限定命名系统允许我们定义具有相同函数原型的接口。在上面的例子中,接口 abc
和 def
包含函数 xyz()
的相同函数原型。类 Demo
实现了这两个接口。使用完全限定名,它定义了特定于接口 abc
的 xyz
实现,以及特定于接口 def
的 xyz
实现。
P16.cs
class Demo : def
{
public static void Main()
{
System.Console.WriteLine("Hello Interfaces");
Demo refDemo = new Demo();
def refdef = refDemo;
refdef.xyz();
refdef.pqr();
}
public void xyz()
{
System.Console.WriteLine("In xyz");
}
public void pqr()
{
System.Console.WriteLine("In pqr");
}
}
interface abc
{
void xyz();
}
interface def : abc
{
void pqr();
}
输出
Hello Interfaces
In xyz
In pqr
上面的程序可以成功编译并运行,产生预期的输出。接口支持继承。接口 def
继承了接口 abc
的原型。类 Demo
实现了接口 def
。接口变量 refdef
存储了类 Demo
对象的引用。通过接口引用变量 refdef
调用了类 Demo
的函数 xyz()
和 pqr()
。
在接口 def
继承自接口 abc
之后,您认为 xyz()
和 pqr()
的完全限定名会是什么?
P17.cs
class Demo : def
{
public static void Main()
{
System.Console.WriteLine("Hello Interfaces");
Demo refDemo = new Demo();
def refdef = refDemo;
refdef.xyz();
refdef.pqr();
}
void def.xyz()
{
System.Console.WriteLine("In xyz");
}
void def.pqr()
{
System.Console.WriteLine("In pqr");
}
}
interface abc
{
void xyz();
}
interface def : abc
{
void pqr();
}
输出
P17.cs(12,8): error CS0539: 'def.xyz' in explicit interface declaration is
not a member of interface
P17.cs(29,11): (Location of symbol related to previous error)
P17.cs(1,7): error CS0535: 'Demo' does not implement interface member
'abc.xyz()'
P17.cs(26,8): (Location of symbol related to previous error)
糟糕!函数 xyz
的原型是接口 abc
的原始成员。因此,即使接口 def
继承自接口 abc
,函数 xyz()
的完全限定名仍然是 abc.xyz
,而不是像上面程序中那样是 def.xyz
。事实上,我们遇到了编译器错误。这可以通过使用函数 xyz()
的正确完全限定名来修复,如下面的程序所示。
P18.cs
class Demo : def
{
public static void Main()
{
System.Console.WriteLine("Hello Interfaces");
Demo refDemo = new Demo();
def refdef = refDemo;
refdef.xyz();
refdef.pqr();
}
void abc.xyz()
{
System.Console.WriteLine("In xyz");
}
void def.pqr()
{
System.Console.WriteLine("In pqr");
}
}
interface abc
{
void xyz();
}
interface def : abc
{
void pqr();
}
输出
Hello Interfaces
In xyz
In pqr
上面的程序可以成功编译并运行,产生预期的输出。但是在使用完全限定函数名时有一些陷阱。
P19.cs
class Demo : def
{
public static void Main()
{
System.Console.WriteLine("Hello Interfaces");
Demo refDemo = new Demo();
refDemo.xyz();
refDemo.pqr();
}
void abc.xyz()
{
System.Console.WriteLine("In xyz");
}
void def.pqr()
{
System.Console.WriteLine("In pqr");
}
}
interface abc
{
void xyz();
}
interface def : abc
{
void pqr();
}
输出
P19.cs(7,5): error CS0117: 'Demo' does not contain a definition for 'xyz'
P19.cs(8,5): error CS0117: 'Demo' does not contain a definition for 'pqr'
上面的程序未能通过编译。refDemo
指向类 Demo
的一个对象。我们正在使用 refDemo()
调用 xyz()
。对于 C# 来说,xyz()
和 ddd.xyz()
是两回事。它知道 abc.xyz()
存在。它也知道类中根本没有单独的 xyz()
。这就是为什么我们会收到一个错误,说类 Demo
没有定义 xyz()
。对于 C# 来说,只有类型为 abc
的接口引用才能调用 abc.xyz()
。同样,只有类型为 def
的接口引用才能调用 def.pqr()
。