C#继承和虚方法virtual
在C#中,继承是一个重要的面向对象编程(OOP)特性,它允许您创建一个类(称为派生类或子类),该类继承另一个类(称为基类或父类)的属性和方法。继承提供了代码复用的机制,使得您可以扩展已有的类来创建新的类,同时保持原有类的功能。
继承的基本概念
- 基类(Base Class):这是被继承的类,也称为父类或超类。基类可以定义一些通用的行为和状态。
- 派生类(Derived Class):这是继承基类的类,也称为子类。派生类可以从基类继承成员,并可以添加新的成员或覆盖已有的成员。
继承的语法
在C#中,继承是通过在类定义后面加上冒号 :
并指定要继承的基类来实现的。
public class Animal // 基类
{
public virtual void MakeSound() // 虚方法
{
Console.WriteLine("Some sound");
}
}
public class Dog : Animal // 派生类
{
public override void MakeSound() // 覆盖基类的方法
{
Console.WriteLine("Woof!");
}
}
继承的关键概念
访问修饰符
- public:公开的,可以从任何地方访问。
- protected:受保护的,只能从派生类访问。
- internal:在同一程序集中可见。
- private:私有的,只能在定义它的类内部访问。
基类中的成员可以被派生类访问,但受限于成员的访问级别。例如,private
成员无法被派生类访问。
虚方法与覆盖
- 虚方法(Virtual Methods):在基类中定义,可以被派生类覆盖。使用
virtual
关键字定义。 - 覆盖方法(Override Methods):在派生类中重写基类的虚方法,以提供不同的实现。使用
override
关键字定义。
public class BaseClass
{
public virtual void VirtualMethod()
{
Console.WriteLine("BaseClass's VirtualMethod");
}
}
public class DerivedClass : BaseClass
{
public override void VirtualMethod()
{
Console.WriteLine("DerivedClass's VirtualMethod");
}
}
虚方法(Virtual Methods)
虚方法是一个在基类中定义的方法,它可以被派生类重写(override)。虚方法使用 virtual
关键字来定义。虚方法的主要目的是为了实现多态性,即让派生类能够提供自己的实现方式。
定义虚方法
虚方法的定义格式如下:
csharp深色版本
public virtual returnType methodName([parameters])
{
// 方法体
}
示例
假设我们有一个 Animal
类,其中定义了一个虚方法 MakeSound
:
csharp深色版本
public class Animal
{
public virtual void MakeSound()
{
Console.WriteLine("The animal makes a sound.");
}
}
在这个例子中,MakeSound
方法是一个虚方法,它提供了一个默认的行为,即打印出一条消息。派生类可以选择覆盖这个方法来提供特定的行为。
覆盖方法(Overridden Methods)
覆盖方法是在派生类中重新定义基类的虚方法的过程。覆盖方法使用 override
关键字来定义,并且必须完全匹配基类中虚方法的签名(包括返回类型和参数列表)。
定义覆盖方法
覆盖方法的定义格式如下:
public override returnType methodName([parameters])
{
// 方法体
}
示例
接下来,我们创建一个 Dog
类,它继承自 Animal
类,并覆盖了 MakeSound
方法:
public class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("The dog says woof!");
}
}
在这个例子中,Dog
类覆盖了 Animal
类中的 MakeSound
方法,提供了不同的实现。
使用虚方法和覆盖方法
当您调用一个虚方法时,实际调用的是对象的实际类型所定义的方法。这就是多态性的体现。即使您使用基类的引用来指向派生类的对象,调用的仍然是派生类中覆盖的方法。
示例
考虑以下代码:
public class Program
{
public static void Main(string[] args)
{
Animal myDog = new Dog(); // 基类引用指向派生类对象
Animal myCat = new Animal(); // 基类引用指向基类对象
myDog.MakeSound(); // 输出 "The dog says woof!"
myCat.MakeSound(); // 输出 "The animal makes a sound."
}
}
在这个例子中,myDog
是一个 Animal
类型的引用,但它指向的是 Dog
类的对象。因此,当调用 MakeSound
方法时,实际上调用的是 Dog
类中覆盖的方法。
虚方法与覆盖方法的区别
- 虚方法:定义在基类中,可以被派生类覆盖。如果没有被派生类覆盖,则使用基类中的实现。
- 覆盖方法:定义在派生类中,用于替代基类中的虚方法实现。必须与基类中的虚方法完全匹配。
注意事项
- 不可覆盖的方法:如果一个方法被声明为
sealed
,则不能被派生类覆盖。 - 必须覆盖的方法:如果一个方法被声明为
abstract
,则必须在派生类中覆盖它。 - 类型安全性:在使用虚方法和覆盖方法时,应确保类型的安全性。例如,不能将派生类的引用强制转换为基类,然后调用派生类特有的方法。
密封方法与密封类
- 密封方法(Sealed Methods):使用
sealed
关键字定义的方法不能被派生类覆盖。 - 密封类(Sealed Classes):使用
sealed
关键字定义的类不能被继承。
csharp深色版本
public sealed class SealedClass
{
// 不能被继承
}
public class BaseClass
{
public virtual void VirtualMethod() { }
public sealed void SealedMethod() { } // 不能被覆盖
}
基类构造函数的调用
在派生类的构造函数中,必须显式地调用基类的一个构造函数。这通常是通过在派生类构造函数中使用 base
关键字来完成的。
public class BaseClass
{
public BaseClass()
{
Console.WriteLine("BaseClass Constructor");
}
}
public class DerivedClass : BaseClass
{
public DerivedClass()
{
// 基类构造函数会在此处被自动调用
Console.WriteLine("DerivedClass Constructor");
}
}
继承的注意事项
- 单继承:C# 支持单继承,即一个类只能直接继承一个基类。
- 多重继承:虽然C#不支持多重继承(一个类继承多个基类),但它通过接口来实现多接口继承。
- 继承层次:继承可以形成层次结构,一个类可以继承另一个派生类。
示例
下面是一个简单的继承示例,展示了如何使用继承来创建类层次结构。
public class Animal
{
public virtual void Sound()
{
Console.WriteLine("Animal makes a sound.");
}
}
public class Dog : Animal
{
public override void Sound()
{
Console.WriteLine("Dog barks.");
}
}
public class Cat : Animal
{
public override void Sound()
{
Console.WriteLine("Cat meows.");
}
}
public class Program
{
public static void Main(string[] args)
{
Animal myDog = new Dog();
Animal myCat = new Cat();
myDog.Sound(); // 输出 "Dog barks."
myCat.Sound(); // 输出 "Cat meows."
}
}
通过这个例子,您可以看到如何定义基类 Animal
和两个派生类 Dog
和 Cat
,以及如何在派生类中覆盖基类的方法。