本文摘自"Introducing Visual C# 2010"(Adam Freeman, Apress, 2010)一书第12章关于介面的内容。

使用介面

介面含有一组用于方法和其它成员的说明。

当一个类别含有在一个介面中定义的所有成员的实现时,称这个类别实现了一个介面。

在以下章节中,将演示如何定义、实现、及使用介面,并解释它们与其它C#特性,如抽象类,有何不同。

何时使用介面和基类

基类允许你在派生类中生成共用的行为,当你希望所有类具有同样的特性并与另一个类相关时,基类是很有用的。你可以在第6章看到相关的示例。

介面允许你跨越无共同先辈的类来生成共同的行为,意即,你可以上溯任何对象,这些对象实现了一个给定介面,并以同样的代码处理它们。

定义和使用一个简单的介面

本节中,我们将考察一个简单的介面,定义一个实现它的类,并演示如何使用这个类。

定义一个介面

清单12-1含有一个简单的介面:

public interface IBasicCalculator 
{ 
  int CalculateSum(int x, int y); 
  int CalculateProduct(int x, int y); 
} 

清单12-1中的介面叫做IBasicCalculator。C#中介面命名的约定是遵循Pascal大小写,并以字母I开头。你可以在第4章看到一个完整的命名约定列表。

interface1  

图12-1描述了这个IBasicCalculator介面。

(注:Modifier —修饰符,Keyword —关键词,Name —介面名,Member Specification —成员说明)

介面用Interface关键词进行定义。你可以对介面运用标准的访问修饰符,修饰符的操作和类的修饰符相同,详见第6章的说明。介面最重要的部分是成员说明。清单12-1中,指定了两个方法:CalculateSum和CalculateProduct。这些说明定义了以两个int整数为参数并返回一个int结果的两个方法。介面可以包含其它种类的成员,将在本章稍后涉及。

所有介面的成员说明都隐含为public(公用),意即,你不可以用访问修饰符来限制对它的访问。

实现一个介面,一旦已经定义了一个介面,你可以生成实现这个介面的类。

清单12-2含有一个类的示例,它实现了清单12-1所定义的介面。

class Calculator : IBasicCalculator 
{ 
  public int CalculateSum(int x, int y) { return x + y; } 
  public int CalculateProduct(int x, int y) { return x * y; } 
} 

实现一个介面就像从一个基类派生一样。把一个冒号(:)放在类名之后,并加上你想实现的介面名。

你必须在你的类中提供这个介面中的每个 成员的实现。在这个例子中,为了实现这个IBasicClaculator介面,Calculator类必须提供CalculatorSum和 CalculateProduct成员的实现。你的类可以实现附加的成员,但必须至少要实现这个介面中的所有成员。在实现一个介面中所定义的方法时,你不需要用override关键词。

使用一个介面,当一个类已经实现了一个介面时,从这个类生成的对象可以上溯到这个介面类型,如清单12-3所示。

class Listing_03 { 
  static void Main(string[] args) 
  { 
    // Create an Object and Upcast it to the interface type 
    IBasicCalculator calc = new Calculator(); 
    // perform some calculations using the interface members 
    int sumresult = calc.CalculateSum(100, 100); 
    int productresult = calc.CalculateProduct(100, 100); 
    // print out the results 
    Console.WriteLine("Sum Result: {0}", sumresult); 
    Console.WriteLine("Product Result: {0}", productresult); 
    // wait for input before exiting 
    Console.WriteLine("Press enter to finish"); 
    Console.ReadLine(); 
  } 
} 

在这个例子中,生成了一个新的Calculator对象,并把它赋给一个IBasicCalculator局部变量实现了上溯。一旦把一个对象上溯给一个接口类型,只有该介面中指定的成员才可以被使用。此例中,调用了CalculateSum和CalculateProduct方法,并列印出了结果。

使用介面获得了基类派生同样的益处。你的代码可以在一个给定的抽象层上工作,可以不必修改现存的代码来定义新类。而且,正如你可能注意到的,我们可以通过使用抽象类获得与清单1-3代码同样的效果(抽象类是在第6章中描述的)。

使用介面的主要优点是可以实现的类不止一个,而这些类不能从一个单一的基类派生(抽象或相反)。事实上,正如我们将从以下小节要看到的,一个类可以有一个基类,并且实现多个介面。

一旦已经定义了一个介面,你可以生成实现这个介面的类。清单12-2含有一个类的示例,它实现了清单12-1所定义的介面。

class Calculator : IBasicCalculator 
{ 
  public int CalculateSum(int x, int y) { return x + y; } 
  public int CalculateProduct(int x, int y) { return x * y; } 
}

实现一个介面就像从一个基类派生一样。把一个冒号(:)放在类名之后,并加上你想实现的介面名。

你必须在你的类中提供这个介面中的每个 成员的实现。在这个例子中,为了实现这个IBasicClaculator介面,Calculator类必须提供CalculatorSum和 CalculateProduct成员的实现。你的类可以实现附加的成员,但必须至少要实现这个介面中的所有成员。在实现一个介面中所定义的方法时,你不需要用override关键词。

使用一个介面,当一个类已经实现了一个介面时,从这个类生成的对象可以上溯到这个介面类型,如清单12-3所示。

class Listing_03 { 
  static void Main(string[] args) { 
    // create an object and upcast it to the interface type 
    IBasicCalculator calc = new Calculator(); 
    // perform some calculations using the interface members 
    int sumresult = calc.CalculateSum(100, 100); 
    int productresult = calc.CalculateProduct(100, 100); 
    // print out the results 
    Console.WriteLine("Sum Result: {0}", sumresult); 
    Console.WriteLine("Product Result: {0}", productresult); 
    // wait for input before exiting 
    Console.WriteLine("Press enter to finish"); 
    Console.ReadLine(); 
  } 
} 

在这个例子中,生成了一个新的Calculator对象,并把它赋给一个IBasicCalculator局部变量实现了上溯。一旦把一个对象上溯给一个接 口类型,只有该介面中指定的成员才可以被使用。此例中,调用了CalculateSum和CalculateProduct方法,并列印出了结果。

使用介面获得了基类派生同样的益处。你的代码可以在一个给定的抽象层上工作,可以不必修改现存的代码来定义新类。而且,正如你可能注意到的,我们可以通过使用抽象类获得与清单1-3代码同样的效果(抽象类是在第6章中描述的)。

使用介面的主要优点是可以实现的类不止一个,而这些类不能从一个单一的基类派生(抽象或相反)。事实上,正如我们将从以下小节要看到的,一个类可以有一个基类,并且实现多个介面。

指定介面成员

一个介面可以为将要实现的类指定方法、属性、事件、和索引子。在本小节中,我将给你示范它们的每一种格式,并演示它们的实现。

指定方法

清单12-1演示了在介面中指定方法。图12-2描述了第一个方法说明。你可以在第6章了解方法的更多内容。

interface2  

当你指定一个方法时,你要提供结果类型(或者如果该方法不返回结果,用void关键词)、你要指定的方法名、以及用于此方法的参数(可选)。

所有介面成员说明都隐含了abstract(抽象)、virtual(虚拟)、和public(公用)。这些关键词在第6章都作了解释。

当一个类实现一个指定了方法的介面时,这个所 实现的方法的返回类型、方法名、以及参数都必须与这个介面所指定的这些对应匹配。该方法的实现必须是public的。你不需要用override关键词来 实现由介面指定的方法。清单12-2演示了一个类实现如图12-2所指定的方法。

指定属性

介面可以指定属性,迫使一个实现该介面的类实现该属性的访问器(指属性的get和set —译者注)之一或两个都实现。属性是在第8章描述的。清单12-4含有指定了一个属性的介面。

public interface IBasicCalculator 
{ 
  int CalculationsPerformedCounter { get; set; } 
  int CalculateSum(int x, int y); 
  int CalculateProduct(int x, int y); 
} 

介面中的属性说明以黑体表示,并如图12-3所示。

interface3  

当你在一个介面中指定一个属性时,你不要提供该属性访问器的实现,也不需要用abstract关键词。正如方法那样,属性也是隐含public的,因而也不需要对此属性使用访问修饰符关键词。

你可以选择性地指定访问器之一,但无论你指定什么访问器,它(它们)都必须由实现这个介面的类来实现。以下是实现清单12-4介面的类:

public class Calculator : IBasicCalculator 
{ 
  private int calcCounter = 0; 
  public int CalculationsPerformedCounter 
  { 
    get { return calcCounter; } 
    set { calcCounter = value; } 
  } 
  public int CalculateSum(int x, int y) 
  { 
    // increment the calculation counter 
    CalculationsPerformedCounter++; 
    // perofrm the calculation and return the result 
    return x + y; 
  } 
  public int CalculateProduct(int x, int y) 
  { 
    // increment the calculation counter 
    CalculationsPerformedCounter++; 
    // perform the calculation and return the result 
    return x * y; 
  } 
} 

清单12-4中所指定的属性的实现如黑体所示。我用一个后台栏位实现了这个属性,但我也可以用一个自动实现的属性来代替。为介面实现的属性必须是public型的。实现一个介面中指定的属性的类可 以自由地在此属性的访问器中添加任意代码,因此你可以使用第8章中讨论的各种技术。

指定一个事件

介面可以指定事件,在一个介面中的一个事件的说明和它在一个类中相应的实现出奇地相似。清单12-5演示了一个指定了事件的介面。你可以在第10章阅读更多关于事件的内容。

public interface IBasicCalculator { 
  event EventHandler CalculationPerformedEvent; 
  int CalculateSum(int x, int y); 
} 

在这个介面中所指定的事件以黑体显示,它的描述如图12-4。

interface4  

与你在一个介面中能够指定的其它成员一样,事件是隐含public的,而且在说明中也不用访问修饰符。以下是实现清单12-5介面的一个类:

class Calculator : IBasicCalculator { 
  public event EventHandler CalculationPerformedEvent; 
  public int CalculateSum(int x, int y) { 
    // calculate the result 
    int result = x + y; 
    // invoke the event 
    CalculationPerformedEvent(this, EventArgs.Empty); 
    // return the result 
    return result; 
  } 
} 

在这个类中实现的事件与介面中的说明是相同的,附带了Public访问修饰符。

指定索引子

剩余的一种你可以在一个介面中实现的成员是索引子。清单12-6含有一个例子。

public interface IBasicCalculator { 
  int this[int x, int y] { get; set; } 
} 

清单12-6中的索引子有两个int参数并返回一个int结果。你可以选择性地指明一个或两个访问器,清单12-6中的说明有两个。图12-5描述了索引子说明。

 

interface5  

与其它成员说明一样,索引子也是隐含abstract和public的,尽管这两个关键词你都不需要用。

 

以下是实现清单12-6介面的一个类:

class Calculator : IBasicCalculator { 
  public int this[int x, int y] { 
    get { return x + y; } 
    set { throw new NotImplementedException(); } 
  } 
} 

这个类使用getter来招待计算,但如果用setter,将弹出一个异常。你可以在第8章阅读关于索引子的内容,在第14章了解异常。

派生介面 Derived Interface

介面可以从其它介面派生而来,与派生类的方式相同。 清单12-7含有一个基介面和一个派生介面。

interface IBaseCalculator { 
  int CalculateSum(int x, int y); 
} 

interface IDerivedCalculator : IBaseCalculator { 
  int CalculateProduct(int x, int y); 
} 

IDerivedCalculator介面派生于IBaseCalculator,你可以通过在介面名后面加一个冒号(:),并指定基介面的方法来派生一个介面。

派生介面继承基介面的所有成员说明。一个实现IDerivedCalculator的类必须实现该介面所指定的CalculatorProduct方法,以及由基介面所指定的CalculateSum方法。以下是实现这个介面的类:

class Calculator : IDerivedCalculator { 
  public int CalculateProduct(int x, int y) { return x * y; } 
  public int CalculateSum(int x, int y) { return x + y; } 
} 

从实现派生介面的类所生成的对象可以上溯到派生介面和基介面类型,像这样:

// create an instance of Calculator and upcast it 
IDerivedCalculator derivedCalc = new Calculator(); 
// upcast the derived interface to the base interface 
IBaseCalculator baseCalc = derivedCalc; 

当你上溯到基介面时,你只可以访问由那个介面所指定的成员,就像上溯到基类那样。

相关文章