配置基类和派生类
基类 和 派生类 术语用于描述继承层次结构中的类之间的关系。 基类用于定义其他类继承的一组常见属性和行为。 派生类用于定义扩展或修改基类的一组专用属性和行为。
了解继承的可传递性质
在 C# 中,类只能从一个基类继承。 此限制称为单一继承。 单个继承允许派生类访问单个基类的属性和方法,从而提升清晰简单的层次结构。 C# 不支持多个继承的概念。 此限制可避免从多个基类继承时可能出现的复杂性和歧义。 相反,C# 使用接口来实现类似的功能。
尽管类只能继承一个基类,但继承是可传递的。 这意味着,如果类 C 继承自类 B,而类 B 继承自类 A,则类 C 继承在类 B 和类 A 中声明的成员。此属性允许更深入的层次结构和进一步的代码重用。
例如,假设车辆管理系统有基类 Vehicle、继承自 Car的派生类 Vehicle,另一个从 ElectricCar继承的派生类 Car。 在此层次结构中,ElectricCar 继承来自 Car 和 Vehicle的成员,这演示了继承的可传递性。
public class Vehicle
{
public string Make { get; set; }
public string Model { get; set; }
public void StartEngine()
{
Console.WriteLine("Engine started.");
}
public void StopEngine()
{
Console.WriteLine("Engine stopped.");
}
}
public class Car : Vehicle
{
public int NumberOfDoors { get; set; }
public void OpenTrunk()
{
Console.WriteLine("Trunk opened.");
}
public void HonkHorn()
{
Console.WriteLine("Horn honked.");
}
public void LockDoors()
{
Console.WriteLine("Doors locked.");
}
}
public class ElectricCar : Car
{
public int BatteryCapacity { get; set; }
public void ChargeBattery()
{
Console.WriteLine("Battery charging.");
}
public void DisplayBatteryStatus()
{
Console.WriteLine("Battery status displayed.");
}
}
public class CombustionEngineCar : Car
{
public int FuelCapacity { get; set; }
public void Refuel()
{
Console.WriteLine("Car refueled.");
}
public void CheckOilLevel()
{
Console.WriteLine("Oil level checked.");
}
}
此示例演示了具有以下类的简单车辆管理系统:
Vehicle类:基类Vehicle包含所有车辆的通用属性和方法,例如 Make、Model、StartEngine 和 StopEngine。Car类:Car类继承自Vehicle,并为汽车添加特定属性和方法,如NumberOfDoors、OpenTrunk、HonkHorn和LockDoors。ElectricCar类:ElectricCar类继承自Car,并添加特定于电动汽车的属性和方法,如BatteryCapacity、ChargeBattery和DisplayBatteryStatus。CombustionEngineCar类:CombustionEngineCar类也继承自Car,并添加特定于燃烧引擎汽车的属性和方法,如FuelCapacity、Refuel和CheckOilLevel。
在此示例中,Car 类从 Vehicle 类继承成员,ElectricCar 和 CombustionEngineCar 从 Car 类继承成员。 此层次结构演示继承的可传递性质,其中 ElectricCar 和 CombustionEngineCar 从 Car 和 Vehicle继承成员。
了解成员继承和可见性
当类从基类继承时,将应用以下规则:
- 静态构造函数和实例构造函数不会继承。
- 基类的所有其他成员都继承,但访问修饰符会影响其在派生类中的可见性。 访问修饰符包括:
public、protected、internal和private。
公共成员
任何可以访问类的代码都可以访问其公共成员。 派生类继承公共成员,并且可以从类层次结构外部进行访问。
public class BaseClass
{
public int publicField;
public void PublicMethod() { }
}
public class DerivedClass : BaseClass
{
public void AccessPublicMember()
{
publicField = 10;
PublicMethod();
}
}
在此示例中,DerivedClass 从 publicField继承 PublicMethod 和 BaseClass 成员。 AccessPublicMember 中的 DerivedClass 方法可以访问这些成员。 公共成员也可以从类层次结构外的代码进行访问。
受保护的成员
受保护的成员可以在声明它们的类和派生类中进行访问。 它们无法从类层次结构外部访问。
public class BaseClass
{
protected int protectedField;
protected void ProtectedMethod() { }
}
public class DerivedClass : BaseClass
{
public void AccessProtectedMember()
{
protectedField = 10;
ProtectedMethod();
}
}
在此示例中,DerivedClass 从 protectedField继承 ProtectedMethod 和 BaseClass 成员。 AccessProtectedMember 中的 DerivedClass 方法可以访问这些成员。 但是,如果尝试从类层次结构外部访问受保护的成员,则会生成编译时错误。
内部成员
内部成员可以在同一程序集中进行访问。 它们无法从程序集外部访问,即使继承类也是如此。
public class BaseClass
{
internal int internalField;
internal void InternalMethod() { }
}
public class DerivedClass : BaseClass
{
public void AccessInternalMember()
{
internalField = 10;
InternalMethod();
}
}
在此示例中,DerivedClass 从 internalField继承 InternalMethod 和 BaseClass 成员。 AccessInternalMember 中的 DerivedClass 方法可以访问这些成员,因为它们位于同一程序集中。 但是,如果尝试从程序集外部访问内部成员,则会生成编译时错误。
专用成员
私有成员只能在声明它们的类中进行访问。 派生类不会继承私有成员,因此无法直接在派生类中访问它们。
public class BaseClass
{
private int privateField;
private void PrivateMethod() { }
}
public class DerivedClass : BaseClass
{
public void AccessPrivateMember()
{
// Can't access privateField or PrivateMethod
}
}
在此示例中,DerivedClass 继承自 BaseClass,但它无法访问 privateField 或 PrivateMethod 成员,因为它们是私有成员。
检查基类中的抽象、虚拟和密封关键字的使用
基类和派生类使用 abstract、virtual和 sealed 关键字来控制继承的成员的行为。 通过这些关键字,可以定义派生类对基类成员拥有的控制级别。
检查 abstract 关键字的使用
C# 中的 abstract 关键字用于定义不完整的类和类成员,必须在派生类中实现。 抽象类不能直接实例化,旨在成为其他类的基类。 抽象方法和属性在声明时没有具体实现,必须在非抽象的派生类中进行重写。
例如:
public abstract class Shape
{
public abstract int GetArea();
}
public class Square : Shape
{
private int _side;
public Square(int side)
{
_side = side;
}
public override int GetArea()
{
return _side * _side;
}
}
class Program
{
static void Main()
{
Square square = new Square(5);
Console.WriteLine($"Area of the square = {square.GetArea()}");
}
}
在此示例中,Shape 类是抽象的,包含抽象方法 GetArea。 Square 类继承自 Shape,并为 GetArea 方法提供实现。 可以实例化 Square 类,GetArea 方法返回正方形的区域。
以下规则描述了 abstract 关键字如何影响继承:
- 抽象类:无法直接实例化抽象类。 抽象类是派生类的基类,派生类必须为抽象类的所有抽象成员提供实现。
- 抽象方法:抽象方法在抽象类中未声明任何实现。 派生类必须重写这些方法并提供具体实现。
- 抽象属性:与抽象方法类似,抽象属性在声明时没有具体实现,必须在派生类中进行重写。
C# 中的 abstract 关键字是用于定义必须在派生类中实现的不完整类和成员的强大工具。 它强制实施派生类必须遵循的协定,确保实现某些方法和属性。 适当地使用 abstract 关键字可提升基类和派生类之间责任的明确描述。
检查 virtual 关键字的使用
C# 中的 virtual 关键字用于定义可在派生类中重写的方法和属性。 虚拟方法或属性在基类中具有实现,但在派生类中可以扩展或修改它。 派生类可以重写虚拟成员以提供它们自己的实现。
例如:
public class Animal
{
public virtual void MakeSound()
{
Console.WriteLine("Animal makes a sound");
}
}
public class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("Dog barks");
}
}
class Program
{
static void Main()
{
Animal animal = new Dog();
animal.MakeSound();
}
}
在此示例中,Animal 类定义 MakeSound 输出泛型消息的虚拟方法。 Dog 类继承自 Animal 并重写 MakeSound 方法以打印特定消息。 当创建 Dog 对象并调用 MakeSound 方法时,将执行 Dog 类中重写的实现。
以下规则描述了 virtual 关键字如何影响继承:
- 虚拟方法:虚拟方法在基类中有实现,但可以在派生类中被重写。
- 虚拟属性:类似于虚拟方法,虚拟属性在基类中具有实现,并且可以在派生类中重写。
检查 sealed 关键字的使用
C# 中的 sealed 关键字用于防止继承或重写类或类成员。 当类被标记为 sealed时,它不能用作其他类的基类。 当方法标记为 sealed时,无法在派生类中重写该方法。
例如:
public class BaseClass
{
public virtual void Method1()
{
Console.WriteLine("Method1 in BaseClass");
}
public virtual void Method2()
{
Console.WriteLine("Method2 in BaseClass");
}
}
public class DerivedClass : BaseClass
{
public sealed override void Method1()
{
Console.WriteLine("Method1 in DerivedClass");
}
public override void Method2()
{
Console.WriteLine("Method2 in DerivedClass");
}
}
public class FinalClass : DerivedClass
{
// This class can't override Method1 because it's sealed in DerivedClass
public override void Method2()
{
Console.WriteLine("Method2 in FinalClass");
}
}
在此示例中,DerivedClass 继承自 BaseClass 并重写 Method1 方法,将其标记为 sealed。 Method2 方法也会在 DerivedClass 中重写,但未密封。 FinalClass 继承自 DerivedClass,并尝试重写 Method2 方法。 但是,它不能重写 Method1 方法,因为它被密封在 DerivedClass 中。
以下规则描述了 sealed 关键字如何影响继承:
- 密封类:密封类不能用作其他类的基类。 它阻止从密封类进行继承。
- 密封方法:不能在派生类中重写密封方法。 它可防止在派生类中进一步修改方法。
- 密封属性:与密封方法类似,不能在派生类中重写密封属性。
如果要防止进一步扩展或修改类或方法,密封类和方法非常有用。 它们提供了一种限制继承的方法,并确保某些成员保持不变。
了解从对象的隐式继承
在 C# 中,所有类都隐式继承自 Object 类。 Object 类定义可用于所有类的多种方法,例如 ToString、Equals和 GetHashCode。 如果类未显式继承自另一个类,则它仍默认从 Object 继承。
ToString:ToString方法返回表示当前对象的字符串。 默认情况下,它返回类的完全限定名称。Equals:Equals方法比较两个对象是否相等。 默认情况下,它会比较对象的引用。GetHashCode:GetHashCode方法返回当前对象的哈希代码。 默认情况下,它返回对象的引用的哈希代码。
请考虑以下代码示例,该示例创建三个 Person 对象,并演示继承的 ToString、Equals和 GetHashCode 方法:
Person person1 = new Person { Name = "Alice", Age = 30 };
Person person2 = new Person { Name = "Alice", Age = 30 };
Person person3 = person1;
Console.WriteLine(person1.ToString());
Console.WriteLine(person1.Equals(person2));
Console.WriteLine(person1.GetHashCode());
Console.WriteLine(person1.Equals(person3));
public class Person
{
public string? Name { get; set; }
public int Age { get; set; }
}
public class Employee : Person
{
public int EmployeeNumber { get; set; }
public decimal Salary { get; set; }
}
// Output: Person
// False
// 32854180
// True
在此代码示例中,ToString 方法返回 Person 类的完全限定名称,其中包括定义的命名空间。 第一个 Equals 方法比较 person1 和 person2 对象的引用,并返回 False。 GetHashCode 方法返回 person1 对象的引用的哈希代码。 然后,Equals 方法比较 person1 和 person3 对象的引用,并返回 True。
总结
继承使派生类能够继承定义一组常见属性和行为的基类成员。 派生类可以通过添加新成员或重写现有成员来扩展或修改基类的行为。 abstract、virtual 和 sealed 关键字用于控制基类成员的继承或重写方式。 继承成员的可见性受访问修饰符的影响,例如 public、protected、internal和 private。 C# 中的所有类都隐式继承自 Object 类,该类提供可用于所有类的多种方法,例如 ToString、Equals和 GetHashCode。