IRONSOFTWAREHOME

了解 C# OOP

理解 C# 面向对象编程

Tim Corey

49分41秒

继承和接口是面向对象编程(OOP)不可或缺的组成部分。 Tim Corey 在他的视频" C# 中的继承与接口:面向对象编程"中,详细解释了何时使用继承以及何时选择接口。

本文作为 Tim Corey 视频的综合指南。 它详细分解了视频中提供的关键概念、示例和代码解释,重点介绍了继承和接口之间的区别以及何时使用哪一个。

简介

Tim 在 (0:00) 首先强调了区分继承和接口的重要性。 他强调,需要了解何时使用每种概念才能取得最佳效果。 他的目标是通过例子来证明这一点,首先是错误地使用单继承,然后进行纠正。

创建项目

在 (1:08) 处,Tim 使用 .NET 5 创建了一个简单的控制台应用程序。他将该项目命名为"OODemoApp",并解释说其主要目标是演示概念,而不是创建可用于生产的代码。

理解遗传

Tim 在 (1:55) 深入探讨了继承的基础知识。 他将继承定义为一种机制,其中基类的属性和方法被派生类继承。 他强调,继承不应该仅仅用于代码重用和共享,而应该用于建立逻辑上的"是"关系。

要点:

  • Is-a 关系:确保派生类是基类的类型。 -公共逻辑:继承的类应该共享公共逻辑,而不仅仅是属性或方法签名。

示例类别:租车

Tim 在 (7:52) 创建了一个 RentalCar 类来说明继承的基本概念。 该类代表佛罗里达州迈阿密一家租赁公司的租赁车辆。

public class RentalCar
{
    public int RentalId { get; set; }
    public string CurrentRenter { get; set; }
    public decimal PricePerDay { get; set; }
    public int NumberOfPassengers { get; set; }

    public void StartEngine()
    {
        Console.WriteLine("Turn key to ignition setting");
        Console.WriteLine("Turn key to on");
    }

    public void StopEngine()
    {
        Console.WriteLine("Turn key to off");
    }
}

展示错误继承的弊端

Tim 在 (10:15) 解释了不当使用继承会导致哪些问题。 他强调,如果继承被滥用,会导致代码难以管理和扩展。 他不建议仅仅为了共享代码而使用继承。

为汽车和卡车类型添加枚举

Tim 在 10:45 添加了汽车类型枚举。他创建了一个名为 Enums.cs 的新类文件,将所有枚举集中在一个地方。 此枚举将有助于区分不同的汽车类型。

// Enums.cs
public enum CarType
{
    Hatchback,
    Sedan,
    Compact
}

然后,他向 RentalCar 类添加了一个属性来指定汽车类型。

public class RentalCar : RentalVehicle
{
    public CarType Style { get; set; }
    // Other properties and methods
}

扩大范围,包括卡车

正如 Tim 在 (12:27) 所解释的那样,租赁公司决定在其车队中增加卡车,这带来了新的要求。 他创建了一个名为 RentalTruck 的类,该类继承自父类 RentalVehicle。

public class RentalTruck : RentalVehicle
{
    public TruckType Style { get; set; }
    // Other properties and methods
}

然后他定义了一个新的卡车类型枚举。

// Enums.cs
public enum TruckType
{
    ShortBed,
    LongBed
}

处理不同类型的房产

Tim 在 (15:28) 强调,仅仅因为两个房产名称相同并不意味着它们是相同的。 他以 Style 属性为例来说明这一点,该属性可以表示不同的枚举值(CarType 表示轿车,TruckType 表示卡车)。

向舰队引入舰艇

租赁公司扩大了其船队规模,新增了船只。 Tim 通过创建一个 RentalBoat 类来演示如何处理这个问题。 起初看来,这似乎可行,因为船只与汽车和卡车有一些共同的特性。

public class RentalBoat : RentalVehicle
{
    // Properties and methods specific to boats
}

处理帆船相关事宜

帆船的引入带来了一项挑战,因为帆船没有发动机。 Tim 在 (19:57) 处阐述了这种情况下继承的局限性。

public class RentalSailboat : RentalVehicle
{
    public override void StartEngine()
    {
        throw new NotImplementedException("I do not have an engine to start");
    }

    public override void StopEngine()
    {
        throw new NotImplementedException("I do not have an engine to stop");
    }
}

利用接口解决问题

Tim 建议将基类中的 StartEngine 和 StopEngine 方法设为虚方法,以便派生类在不使用这些方法时可以进行重写。

public abstract class RentalVehicle
{
    // Common properties

    public virtual void StartEngine()
    {
        Console.WriteLine("Engine started");
    }

    public virtual void StopEngine()
    {
        Console.WriteLine("Engine stopped");
    }
}

不应该调用的处理方法

Tim 在 (21:56) 解释了继承类中不应该调用的方法的弊端。 例如,RentalSailboat 类没有引擎,它继承了 RentalVehicle 类的 StartEngine 和 StopEngine 方法。 如果无意中调用了这些方法,则可能会出现问题,因为它们必须抛出异常来表明它们不适用。

public class RentalSailboat : RentalVehicle
{
    public override void StartEngine()
    {
        throw new NotImplementedException("I do not have an engine to start");
    }

    public override void StopEngine()
    {
        throw new NotImplementedException("I do not have an engine to stop");
    }
}

认识到继承的局限性

Tim 在 (24:06) 强调,当继承不再具有逻辑意义时,它会导致代码库变得复杂混乱。 例如,帆船不应被视为带有发动机的租赁车辆。这表明了继承的局限性以及采用更优设计方法的必要性。

迈向更优的界面设计

为了解决这些问题,Tim 建议采用使用界面的更好设计。 他首先创建了一个名为"BetterOODemo"的新控制台应用程序项目,以演示改进后的方法。

定义接口

Tim 引入了 IRental 接口,用于封装所有出租房共有的属性。

public interface IRental
{
    int RentalId { get; set; }
    string CurrentRenter { get; set; }
    decimal PricePerDay { get; set; }
}

创建陆地车辆基础类

然后,蒂姆创建了一个陆地车辆的基础类,将车辆租赁的概念与车辆本身分开。

public abstract class LandVehicle
{
    public int NumberOfPassengers { get; set; }

    public virtual void StartEngine()
    {
        Console.WriteLine("Engine started");
    }

    public virtual void StopEngine()
    {
        Console.WriteLine("Engine stopped");
    }
}

通过将基础车辆类重命名为 LandVehicle,Tim 确保只有合适的车辆才能继承与发动机相关的方法。

实施轿车和卡车分类

Tim 创建了 Car 和 Truck 类,它们继承自 LandVehicle 类并实现了 IRental 接口。

public class Car : LandVehicle, IRental
{
    public int RentalId { get; set; }
    public string CurrentRenter { get; set; }
    public decimal PricePerDay { get; set; }
    public CarType Style { get; set; }
}

public class Truck : LandVehicle, IRental
{
    public int RentalId { get; set; }
    public string CurrentRenter { get; set; }
    public decimal PricePerDay { get; set; }
    public TruckType Style { get; set; }
}

这种设计保持了清晰的关注点分离,确保每个类只具有与其类型相关的属性和方法。

避免代码重复

Tim 在 (31:41) 讨论了避免不必要的代码重复的重要性。 他解释说,虽然 IRental 接口要求多个类中具有相同的属性,但这并不被认为违反了 DRY(不要重复自己)原则,因为没有逻辑重复——只有属性声明。

实施租赁帆船课程

Tim 在 (35:09) 解释了如何通过实现 IRental 接口而不是继承 LandVehicle 来单独处理 RentalSailboat 类。 这种方法有助于避免因继承不当而带来的种种弊端。

public class Sailboat : IRental
{
    public int RentalId { get; set; }
    public string CurrentRenter { get; set; }
    public decimal PricePerDay { get; set; }

    // Additional properties and methods specific to sailboats
}

管理不同的租赁

Tim 创建了一个列表来管理不同类型的租赁,利用 IRental 界面存储各种租赁类型,包括卡车、帆船和汽车。

List<IRental> rentals = new List<IRental>
{
    new Truck { CurrentRenter = "Truck Renter" },
    new Sailboat { CurrentRenter = "Sailboat Renter" },
    new Car { CurrentRenter = "Car Renter" }
};

此设计允许循环遍历租赁,并访问诸如RentalId等常见属性。

foreach (var rental in rentals)
{
    Console.WriteLine($"Renter: {rental.CurrentRenter}, Price Per Day: {rental.PricePerDay}");
}

铸造成特定类型

为访问不同租赁类型的特定属性和方法,蒂姆演示了如何使用is关键字来检查和转换对象为相应类型。

foreach (var rental in rentals)
{
    if (rental is Truck truck)
    {
        Console.WriteLine($"Truck Style: {truck.Style}, Passengers: {truck.NumberOfPassengers}");
    }
    else if (rental is Sailboat sailboat)
    {
        // Access sailboat-specific properties
    }
    else if (rental is Car car)
    {
        // Access car-specific properties
    }
}

接口的灵活性

Tim强调,使用接口可以为未来的变化提供灵活性。 例如,增加新的租赁类型,如水箱或电视机,不会扰乱现有的结构。

避免过度使用继承

Tim 建议不要过度使用继承进行代码共享,因为这会导致代码库变得复杂且缺乏灵活性。 相反,他建议利用接口和组合来实现预期结果,而无需将"is-a"关系延伸到其逻辑范围之外。

结论

Tim Corey 对面向对象编程中的继承和接口的解释,为创建可维护和灵活的代码提供了一条清晰的途径。 他通过展示常见的陷阱并提供精细的界面设计,确保开发人员能够就如何有效地构建应用程序做出明智的决定。

想要更深入地了解这些概念并观看它们的实际应用,请观看 Tim 的完整视频。 他的频道简直是编程教程的宝库。 别错过!

Earn More by Sharing What You Love

Do you create content for developers working with .NET, C#, Java, Python, or Node.js? Turn your expertise into extra income!

Let's Stay in Touch!

Join our newsletter, you’ll get exclusive access on article updates. We value your privacy

Key in blue circle

立即获取免费的 30 天试用版密钥

Your trial license will be sent to your email address

无任何限制。100% 解锁。无需信用卡。

OR
bullet_checked无需信用卡或创建账户无任何限制。100% 解锁。无需信用卡。
  • Logo Aetna
  • Logo NASA
  • Logo GE
  • Logo Porsche
  • Logo USDA
  • Logo Qatar
Join Millions of Engineers who’ve tried Iron Suite
获取您的无义务咨询
填写下面的表格或通过sales@ironsoftware.com
您的资料将始终保密。
深受全球数百万工程师信赖
Iron Software 的客户徽标
立即获取您的免费30 天试用密钥
无需信用卡或创建账户