c#笔记(二)

C#中的虚方法(virtual method)、接口(interface)、抽象类(abstract class)和密封类(sealed class)。

虚方法

基类中的方法用virtual关键字声明,衍生类中使用override来重写,从而提供该方法的新的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
namespace System
{
class Object
{
public virtual string ToString()
{
// ...
}
// ...
}
// ...
}

class Horse : Mammal
{
// ...
public override string ToString()
{
// base.ToString();
// ...
}
}

使用virtualoverride可以声明多态性的方法。

一些注意事项:

  • 虚方法不能私有。虚方法的目的就是通过继承向其它类公开。类似地重写方法也不能私有,因为类不能改变它继承的方法的保护级别。但是重写方法可以用protected关键字实现所谓的“受保护”保密性;
  • 虚方法和重写方法必须有完全一致的签名。相同的函数名称、参数类型、数量、相同类型的返回值;
  • 只能重写虚方法。对基类的非虚方法进行重写会出现编译时错误;
  • 如果衍生类不用override关键字声明,则不是重写方法,而是隐藏,即成为和基类方法完全无关的另一个方法,该方法只是恰巧和基类方法同名。若如此做会出现编译时警告,称该方法会隐藏继承的同名方法,使用new关键字可以消除此警告;
  • 重写方法隐式地成为虚方法,可以在衍生类中被重写;

接口

System内定义的IComparable接口:

1
2
3
4
interface IComparable
{
int CampareTo(Object obj);
}

声明接口

  • 接口中不能包含字段;
  • 声明时不要加任何访问修饰符;
  • 接口内的方法没有实现,而是用一个分号代替;

实现接口

  • 类或结构实现接口时,必须实现接口中的所有方法;

  • 函数名、返回值类型,参数列表,参数的类型或关键字都完全匹配;

  • 用于实现接口的所有方法都必须有public可访问性(后边提到的“显式接口实现”是一个特例,不需要访问修饰符);

  • 类可以有一个基类,但是可以实现多个接口,声明类时在冒号之后,基类写在接口的前边:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    interface ILandBound
    {
    int NumberOfLegs();
    }
    class Mammal
    {
    // ...
    }
    class Horse : Mammal,ILandBound
    {
    //...
    }

通过接口来引用类

  • 类似于基类对象引用衍生类的实例;

    1
    2
    Horse h = new Horse(...);
    ILandBound i = h;

  • 可以用is运算符来判断某个对象是否是实现了指定接口的类的一个实例;

    1
    2
    3
    4
    5
    if(h is ILandBound)
    {
    ILandBound i = h;
    // ...
    }

使用多个接口

1
2
3
4
5
6
7
8
interface IGrazable
{
// ...
}
class Horse : Mammal,ILandBound,IGrazable
{
//...
}

显式接口实现

  • 某个类实现的多个接口中有相同的函数,但是这些函数表达的不同的语义:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
interface IJourney
{
int NumberOfLegs();
}
class Horse : ILandBound, IJoueny
{
int ILandBound.NumberOfLegs()
{
return 4;
}

int IJoueny.NumberOfLegs()
{
return 3;
}
}

接口的限制

  • 不能在接口中包含任何字段;
  • 不能在接口中定义任何的构造器;
  • 不能在接口中定义任何的析构器;
  • 不能为任何方法指定访问修饰符,接口中的方法都隐式为公有方法;
  • 不能在接口中嵌套任何类型(如枚举、结构、类或其它接口);
  • 接口可以继承接口,但是接口不能继承结构或类;

抽象类

为了明确声明不允许创建某个类的实例,必须将那个类声明为抽象类,用abstract关键字实现的。

1
2
3
4
abstract class GrazingMammal : Mammal, IGrazable
{
// ...
}

抽象方法

抽象类中可以包含抽象方法,使用abstract关键字修饰:

  • 抽象方法原则上与虚方法相似,只是它不包含方法主体;
  • 派生类必须重写抽象方法;
  • 抽象方法不可以私有;

密封类

使用sealed关键字指定某个类为密封类,即该类不能作为基类衍生出新的类。

1
2
3
4
sealed class Horse : GrazingMammal, ILandBound
{
//...
}
  • 密封类中不能声明任何虚方法;
  • 抽象类不能密封;

密封方法

可以用sealed关键字声明非密封类中的一个单独的方法是密封的,即衍生类不能重写该方法。只有用override关键字声明的方法才能密封,而且方法要声明为sealed override

以下是interfacevirtualoverridesealed关键字的一种理解方式:

  • interface引入方法的名称
  • virtual方法是方法的第一个实现
  • override方法是方法的另一个实现
  • sealed是方法的最后一个实现

小结

接口、类和结构定义方法时,各种有效(yes)、无效(no)或必须(required)的关键字组合:

关键字 接口 抽象类 密封类 结构
abstract no yes no no no
new yes[1] yes yes yes no[2]
override no yes yes yes no[3]
private no yes yes yes yes
protected no yes yes yes no[4]
public no yes yes yes yes
sealed no yes yes required no
virtual no yes yes no no

备注

  • [1] 接口可以扩展另一个接口,并引入一个具有相同签名的新方法
  • [2] 结构不支持继承,所以不能隐藏方法
  • [3] 结构不支持继承,所以不能重写方法
  • [4] 结构不支持继承,但结构隐式密封,所以不能从它派生

REFERENCE

Visual C#从入门到精通(第8版)