C#中类的继承与派生在性质上类似C++,但在有些方面有一些区别。
C#中所有类都派生自object类
除了特殊的类object,所有的类都是派生类,即使他们没有基类规格说明,类object是唯一的非派生类,因为他是继承层次结构的基础,与C++不同(C++中允许多继承),在C#中一个类声明的基类规格说明中只能有一个单独的类,也就是只允许单继承,虽然类只能直接继承一个基类,但继承的层次没有限制。在写法上也与C++不同,C#中不存在公有继承、私有继承等这些继承方式,所以在写的时候冒号后面直接接上基类的名字即可,例如 class A : B
屏蔽基类成员
虽然子类不能删除它继承的任何成员,但可以用与父类成员名称相同的成员来屏蔽(mask)父类成员,这是继承的主要功能之一。
屏蔽的使用:
(1)屏蔽一个继承的数据成员,需要声明一个新的相同类型的成员,并使用相同名称
(2)屏蔽一个继承的函数成员,需要声明一个带有相同签名的函数成员,签名由名称和参数列表组成,不包括返回类型
(3)要让编译器知道你在故意屏蔽继承的成员,可以使用new修饰符,否则,程序可以成功编译,但编译器会警告你隐藏了一个继承的成员
(4)也可以屏蔽静态成员
class A
{
public int i;
public void foo(){ }
}
class B :A
{
new public int i;
public void foo() { } //不加new会有一个警告
}
基类访问
表达式:base.成员
虚方法(virtual)和覆写方法(override)
类似C++中的多态的产生,C++中使用子类对象给父类指针赋值,当该父类指针调用虚函数时,就会产生多态,同理C#中使用子类对象的栈上引用给父类引用赋值,当父类引用调用虚方法时,就会产生多态。
class A
{
public virtual void foo()
{
Console.WriteLine("父类");
}
}
class B :A
{
public override void foo()
{
Console.WriteLine("子类");
}
}
class Program
{
static void Main(string[] args)
{
B b = new B();
A a = (A)b;
a.foo(); //输出子类
}
}
virtual和override修饰符的使用
父类中被覆写的方法用virtual来修饰,子类中覆写后的方法需要用override来修饰
(1)覆写和被覆写的方法必须有相同的可访问性
(2)不能覆写static方法或非虚方法
(3)方法、属性、索引器以及事件,都可以被声明为virtual和override
virtual与override的内部调用机制
当使用对象基类部分的引用调用一个覆写的方法时,方法的调用被沿派生层次上溯执行,取寻找标记为override的方法,如果在更高的派生级别有该方法的其他声明,但没有被标记为override,那么它们不会被调用
class A
{
public virtual void foo()
{
Console.WriteLine("A");
}
}
class B :A
{
public override void foo()
{
Console.WriteLine("B");
}
}
class C:B
{
public override void foo()
{
Console.WriteLine("C");
}
}
class Program
{
static void Main(string[] args)
{
C c = new C();
A a = (A)c;
a.foo(); //输出C
}
}
此时a调用foo函数,会根据继承关系向上寻找override方法,在C中有此方法则会调用到C中的foo所以打印出C
class C:B
{
public new void foo()
{
Console.WriteLine("C");
}
}
class Program
{
static void Main(string[] args)
{
C c = new C();
A a = (A)c;
a.foo(); //输出B
}
}
如果将C中的foo函数使用new来修饰,而不是override,则在调用时找不到C中的override修饰的foo函数,则只能调用B中被override修饰的foo函数所以输出为B。