跳转至内容

C# 编程/继承

来自维基教科书,开放的书籍,开放的世界

类比解释

[编辑 | 编辑源代码]

继承的好处是什么?

  1. 它为你节省了很多打字。
  2. 它可以避免你重复自己。

继承的类比解释

假设你想创建一个鹰、一只猎鹰和一只秃鹫。为了创建这些飞行动物,你注意到这些生物都有以下特点:

  1. 飞行
  2. 繁殖
  3. 进食

假设这三种类型的鸟类以完全相同的方式飞行、繁殖和进食。

如果没有继承,你将被迫复制代码。即,让鹰飞行的相同代码也将被复制以使秃鹫飞翔。对于程序员来说,这是一个公理——他们是一群懒惰的人,不想重复自己——重复几乎总是件坏事。

请注意,鹰、猎鹰和秃鹫实际上都是鸟类。因此,可以说鸟类通常总是有进食、繁殖和飞行的特征。所以,使用“继承”,你可以创建一个通用的“鸟类”原型,它可以进食、繁殖和飞行,然后定义好后,你可以让所有其他特定的鸟类继承这些特征。换句话说,使用原型,你可以根据这个原型设计其他特定的鸟类。

这意味着猎鹰会自动知道如何飞行,因为它从通用“鸟类”类继承了这种行为。你基本上不必重复自己。

继承是能够从另一个类(“父”类)创建类,扩展父类在派生类或“子”类中的功能和状态的能力。它允许派生类重载来自其父类的函数。

继承是面向对象编程的支柱之一。它是从另一个类设计一个类的机制,是代码可重用的理念之一,支持层次分类的概念。C# 程序由类组成,其中新的类可以从头开始创建,也可以使用现有类的某些或所有属性来创建。

与继承和代码可重用性相关的另一个特性是多态性,它允许对不同数据类型进行不同操作时使用相同的函数名称。因此,C# 通过这两个特性支持代码可重用性。

继承的重要特征包括:

  1. 派生类扩展其基类。也就是说,它包含其父类的函数和数据,并且它还可以包含自己的数据成员和函数。
  2. 派生类不能更改继承成员的定义。
  3. 构造函数和析构函数不会被继承。基类的所有其他成员都会被继承。
  4. 派生类中成员的可访问性取决于其在基类中声明的可访问性。
  5. 派生类可以重写继承的成员。

继承的示例

using System;
using System.Text;

namespace ContainmentInheritance
{
    class Room
    {
        public int length;
        public int width;
        public int height;

        public Room(int l, int w, int h)
        {
            length = l;
            width = w;
            height = h;
        }
    }

    class Home
    {
        int numberOfRooms;
        int plotSize;
        string locality;
        string name;

        // create an object of class Room inside class Home
        Room studyRoom = new Room(10, 12, 12);

        public Home()
        {
            numberOfRooms = 1;
            plotSize = 1000;
            locality = "Versova";
            name = "study room";
        }

        public void Display()
        {
            Console.WriteLine("MyHome has {0} rooms", numberOfRooms);
            Console.WriteLine("Plot size is {0}", plotSize);
            Console.WriteLine("Locality is {0}", locality);

            int area = studyRoom.length*studyRoom.width;
            Console.WriteLine("Area of the {0} room is {1}", name, area);
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            Home myhome = new Home();
            myhome.Display();

            Console.ReadLine();
        }
    }
}

子类型继承

[编辑 | 编辑源代码]

下面的代码示例展示了两个类,EmployeeExecutiveEmployee 具有 GetPayCheckWork 函数。

我们希望 Executive 类拥有相同的函数,但实现方式不同,并且有一个额外的函数,AdministerEmployee

以下是第一个要派生的类的创建。

public class Employee
{
    // We declare one method virtual so that the Executive class can
    // override it.
    public virtual void GetPayCheck()
    {
        // Get paycheck logic here.
    }

    //Employee's and Executives both work, so no virtual here needed.
    public void Work()
    {
        // Do work logic here.
    }
}

现在,我们创建一个 Executive 类,它将覆盖 GetPayCheck 函数

public class Executive : Employee
{
    // The override keyword indicates we want new logic behind the GetPayCheck method.
    public override void GetPayCheck()
    {
        // New getpaycheck logic here.
    }

    // The extra method is implemented.
    public void AdministerEmployee()
    {
        // Manage employee logic here
    }
}

你会注意到,Executive 类中没有 Work 函数,因为它继承自 Employee

static void Main(string[] args)
{
    Employee emp = new Employee();
    Executive exec = new Executive();

    emp.Work();
    exec.Work();
    emp.GetPayCheck();
    exec.GetPayCheck();
    exec.AdministerEmployee();
}

虚方法

[编辑 | 编辑源代码]

如果基类包含一个虚方法,它在其他地方调用该方法,并且派生类覆盖了该虚方法,那么基类实际上会调用派生类的函数。

public class Resource : IDisposable
{
    private bool _isClosed = false;    // good programming practice initialise, although default
    
    protected virtual void Close()
    {
        Console.WriteLine("Base resource closer called!");
    }
    
    ~Resource()
    {
        Dispose();
    }
    
    public void Dispose()
    {
        if (!_isClosed)
        {
            Console.WriteLine("Disposing resource and calling the Close() method...");
            _isClosed = true;
            Close();
        }
    }
}

public class AnotherTypeOfResource : Resource
{
    protected override void Close()
    {
        Console.WriteLine("Another type of resource closer called!");
    }
}

public class VirtualMethodDemo
{
    public static void Main()
    {
        Resource res = new Resource();
        AnotherTypeOfResource res2 = new AnotherTypeOfResource();
        
        res.Dispose();  // Resource.Close() will be called.
        res2.Dispose(); // Even though Dispose() is part of the Resource class, 
                        // the Resource class will call AnotherTypeOfResource.Close()!
    }
}

构造函数

[编辑 | 编辑源代码]

派生类不会自动继承基类的构造函数,并且它无法实例化,除非它提供自己的构造函数。派生类必须使用 base 关键字调用其基类的构造函数之一。

public class MyBaseClass
{
    public MyBaseClass(string text)
    {
        console.WriteLine(text);
    }
}

public class MyDerivedClass : MyBaseClass
{
    public MyDerivedClass(int number)
        : base(number.ToString())
    { }
    
    public MyDerivedClass(string text) // even though this is exactly the same as MyBaseClass'  
    // only constructor, this is still necessary as constructors are not inherited.
        : base(text)
    { }
}

继承关键字

[编辑 | 编辑源代码]

C# 从另一个类继承的语法方法是使用 : 运算符。

示例

public class Executive : Employee

要指示可以重写的函数,请使用 virtual 标记函数。

public virtual void Write(string text)
{
    System.Console.WriteLine("Text:{0}", text);
}

要覆盖一个函数,请使用 override 关键字

public override void Write(string  text)
{
    System.Console.WriteLine(text);
}

派生函数中缺少 newoverride 关键字可能会导致编译期间出现错误或警告:[1] 以下是一个示例

abstract class ShapesA
{
    abstract public int Area(); // abstract!
}

class Square : ShapesA
{
    int x, y;

    public int Area() // Error: missing 'override' or 'new'
    {
        return x * y;
    }
} 

class Shapes
{
    virtual public int Area() { return 0; } // it is virtual now!
}

class Square : Shapes
{
    int x, y;

    public int Area() // no explicit 'override' or 'new' required
    { return x * y; }
}

Square 类函数 Area() 将导致编译错误,如果它从 ShapesA 类派生

error CS0534: 'ConsoleApplication3.Square' does not implement inherited abstract member
'ConsoleApplication3.Shapes.Area()'

如果从普通的 Shapes 类派生,则相同的函数会导致编译警告

warning CS0114: 'ConsoleApplication3.Square.Area()' hides inherited member 'ConsoleApplication3.Shapes.Area()'.
To make the current member override that implementation, add the override keyword. Otherwise add the new
keyword.

参考资料

[编辑 | 编辑源代码]
  1. Greg Beech (2010-03-09). "C# 设计:为什么抽象方法需要 new/override,而虚拟方法不需要? / 答案". http://efreedom.com/: eFreedom. 检索于 2011-08-11. 使用 .NET 3.5 SP1 中附带的 C# 3.0 编译器或 .NET 4.0 中附带的 C# 4.0 编译器,我都会在你的第一个例子中收到以下错误:[...],在第二个例子中收到以下警告:[...]。在第一个例子中,这是一个错误,因为你实际上并没有重写基方法,这意味着在具体类中没有抽象方法的实现。在第二个例子中,这是一个警告,因为代码在技术上是正确的,但编译器怀疑这不是你想要的。这是通常启用“将警告视为错误”编译设置的一个原因。 {{cite web}}: 外部链接在 |location= (帮助)

华夏公益教科书