Bootstrap

C# 继承


在这里插入图片描述

一、继承的基本概念与意义

  当创建一个新类时,无需一切从零开始,重新编写所有的数据成员和成员函数。通过继承机制,只需巧妙地设计一个新类,使其继承已有的类的成员,就能快速构建起具有特定功能的类结构。在这里,那个已有的类被称作基类(也常被称为父类),而新创建的类则被叫做派生类(亦称为子类)。这种基于继承的设计思想,精准地实现了 “属于(IS-A)” 关系,就如同在现实世界里,哺乳动物从属于动物范畴,狗又属于哺乳动物,所以狗自然也属于动物一样,这种逻辑关系在程序代码中通过类的继承得以清晰体现,让程序的结构与现实世界中的对象关系相互呼应,更符合人们的认知习惯,也使得代码更易于理解和维护。

二、基类与派生类

(一)继承规则与语法

  一个类可以继承自另一个类,形成基类(父类)和派生类(子类)的关系。不过需要注意的是,C# 并不支持类的多重继承,也就是说,一个类只能有一个直接的基类。但它支持接口的多重继承,一个类可以实现多个接口,概括来讲就是:一个类能够继承多个接口,然而只能继承自一个类。
  创建派生类在 C# 中有特定的语法格式,如下所示:

<访问修饰符> class <基类>
{
   ...
}
class <派生类> : <基类>
{
   ...
}

  按照这样的语法规则,派生类会默认继承基类的成员,这里的成员涵盖了字段、方法、属性等诸多方面,不过前提是这些成员没有被明确地标记为私有(private)。因为私有成员的访问权限仅限于其所属的类内部,派生类是无法直接访问的。
例如,以下是一个简单的基类与派生类示例:

class BaseClass
{
    public void SomeMethod()
    {
        // Method implementation
    }
}

class DerivedClass : BaseClass
{
    public void AnotherMethod()
    {
        // 通过关键字 base 调用基类的方法
        base.SomeMethod();

        // Method implementation
    }
}

  在这个示例中,DerivedClass 继承自 BaseClass,在 DerivedClass 的 AnotherMethod 方法里,借助 base 关键字就能顺利调用 BaseClass 中的 SomeMethod 方法,实现了派生类对基类功能的复用和扩展。

(二)通过实例深入理解继承关系

  我们来看一个更具体的例子,假设有一个基类 Shape,以及它的派生类 Rectangle:

using System;
namespace InheritanceApplication
{
    class Shape
    {
        public void setWidth(int w)
        {
            width = w;
        }
        public void setHeight(int h)
        {
            height = h;
        }
        protected int width;
        protected int height;
    }

    // 派生类
    class Rectangle : Shape
    {
        public int getArea()
        {
            return (width * height);
        }
    }

    class RectangleTester
    {
        static void Main(string[] args)
        {
            Rectangle Rect = new Rectangle();

            Rect.setWidth(5);
            Rect.setHeight(7);

            // 打印对象的面积
            Console.WriteLine("总面积: {0}", Rect.getArea());
            Console.ReadKey();
        }
    }
}

  在上述代码中,Shape 类作为基类,定义了设置宽度和高度的方法以及相应的受保护成员变量,而 Rectangle 类继承了 Shape 类后,便能够直接使用基类的方法来设置宽高,并且利用继承过来的成员变量计算自身的面积。当代码被编译和执行时,会输出相应的面积结果,展示了派生类如何借助继承来复用基类的功能并实现自身特定的逻辑。

三、基类的初始化

  派生类继承了基类的成员变量和成员方法,从对象创建的顺序来讲,父类对象应在子类对象创建之前被创建,这是确保继承关系下对象初始化正确的关键。在 C# 中,我们可以在成员初始化列表中进行父类的初始化操作,以此来保证基类部分能按照预期进行初始化设置。
以下面这个程序为例来详细说明:

using System;
namespace RectangleApplication
{
    class Rectangle
    {
        // 成员变量
        protected double length;
        protected double width;
        public Rectangle(double l, double w)
        {
            length = l;
            width = w;
        }
        public double GetArea()
        {
            return length * width;
        }
        public void Display()
        {
            Console.WriteLine("长度: {0}", length);
            Console.WriteLine("宽度: {0}", width);
            Console.WriteLine("面积: {0}", GetArea());
        }
    }//end class Rectangle
    class Tabletop : Rectangle
    {
        private double cost;
        public Tabletop(double l, double w) : base(l, w)
        { }
        public double GetCost()
        {
            double cost;
            cost = GetArea() * 70;
            return cost;
        }
        public void Display()
        {
            base.Display();
            Console.WriteLine("成本: {0}", GetCost());
        }
    }
    class ExecuteRectangle
    {
        static void Main(string[] args)
        {
            Tabletop t = new Tabletop(4.5, 7.5);
            t.Display();
            Console.ReadLine();
        }
    }
}

  在这段代码中,Tabletop 类继承自 Rectangle 类,在 Tabletop 类的构造函数中,通过 : base(l, w) 的方式调用了基类 Rectangle 的构造函数,传递相应的参数来初始化基类的成员变量。这样,当创建 Tabletop 类的对象时,先会确保基类部分得到正确初始化,然后再进行派生类自身的初始化和其他相关操作。当代码被编译并执行后,会依次输出长度、宽度、面积以及成本等信息,清晰展示了基类初始化在整个继承体系中的作用和执行效果。

四、继承接口(Interface Inheritance)

(一)接口继承的规则与特点

  接口在 C# 的继承体系中有着独特的地位和规则。一个接口可以继承自一个或多个其他接口,派生接口会继承基接口的所有成员。并且,派生接口虽然可以扩展基接口的成员列表,添加新的方法等成员,但不能改变它们的访问修饰符,这保证了接口继承过程中的一致性和规范性。
例如,以下代码展示了接口继承的基本形式:

interface IBaseInterface
{
    void Method1();
}

interface IDerivedInterface : IBaseInterface
{
    void Method2();
}

  在这个示例中,IDerivedInterface 接口继承自 IBaseInterface 接口,它继承了 Method1 方法,同时又新增了 Method2 方法,形成了接口之间的继承扩展关系。

(二)接口继承的实例应用

  我们通过一个完整的实例来看接口继承是如何在实际代码中实现和运用的:

using System;

// 定义一个基接口
interface IBaseInterface
{
    void Method1();
}

// 定义一个派生接口,继承自基接口
interface IDerivedInterface : IBaseInterface
{
    void Method2();
}

// 实现派生接口的类
class MyClass : IDerivedInterface
{
    public void Method1()
    {
        Console.WriteLine("Method1 implementation");
    }

    public void Method2()
    {
        Console.WriteLine("Method2 implementation");
    }
}

class Program
{
    static void Main(string[] args)
{
        // 创建 MyClass 类的实例
        MyClass obj = new MyClass();

        // 调用继承自基接口的方法
        obj.Method1();

        // 调用派生接口新增的方法
        obj.Method2();
    }
}

  在这个实例中,MyClass 类实现了 IDerivedInterface 接口,由于接口继承的特性,它必须提供 IDerivedInterface 中定义的所有方法,这其中自然也包括从 IBaseInterface 继承而来的 Method1 方法。在 Main 方法中,创建了 MyClass 的实例 obj 并分别调用了相应的方法,最终会输出对应方法的实现内容,直观展示了接口继承在类实现接口过程中的具体表现和使用方式。

五、C# 多重继承

(一)多重继承的概念与 C# 的实现方式

  多重继承指的是一个类别可以同时从多于一个父类继承行为与特征的功能,与之相对的是单一继承,即一个类别只可以继承自一个父类。然而,C# 本身并不直接支持类的多重继承,这是出于避免复杂的菱形继承等问题所带来的潜在风险以及维护代码清晰性、稳定性的考虑。
  不过,在 C# 中可以巧妙地利用接口来实现类似多重继承的效果。通过让一个类实现多个接口,就能从不同接口中获取相应的方法定义等成员,从而达到复用多个不同接口所规定功能的目的。
  以下面这个程序为例来演示这种通过接口实现多重继承的方式:

using System;
namespace InheritanceApplication
{
    class Shape
{
        public void setWidth(int w)
        {
            width = w;
        }
        public void setHeight(int h)
        {
            height = h;
        }
        protected int width;
        protected int height;
    }

    // 基类 PaintCost(这里以接口形式存在)
    public interface PaintCost
    {
        int getCost(int area);

    }
    // 派生类
    class Rectangle : Shape, PaintCost
    {
        public int getArea()
        {
            return (width * height);
        }
        public int getCost(int area)
        {
            return area * 70;
        }
    }
    class RectangleTester
    {
        static void Main(string[] args)
        {
            Rectangle Rect = new Rectangle();
            int area;
            Rect.setWidth(5);
            Rect.setHeight(7);
            area = Rect.getArea();
            // 打印对象的面积
            Console.WriteLine("总面积: {0}", Rect.getArea());
            Console.WriteLine("油漆总成本: ${0}", Rect.getCost(area));
            Console.ReadKey();
        }
    }
}

  在上述代码中,Rectangle 类一方面继承了 Shape 类,获取了设置宽高以及相关成员变量等功能,另一方面又实现了 PaintCost 接口,拥有了计算成本的方法定义。这样,Rectangle 类就综合了不同 “来源” 的功能,在实际使用中既能计算自身的面积,又能根据面积算出油漆总成本等相关信息。当代码被编译和执行后,会输出相应的面积和成本结果,体现了通过接口实现类似多重继承效果的具体应用场景和优势。
在这里插入图片描述

;