深入理解C#之继承

深入理解C#之继承

继承是面向对象编程中非常重要的一种机制,它可以使得我们代码重用性更高,更加简洁。在C#中,继承可以通过类之间的关系来实现(类之间的关系有继承、实现、组合等)。在本篇文章中,我们将学习C#中的继承机制,重点包括如下内容:

  • 继承的语法和基本概念
  • 虚方法和抽象类
  • 接口继承
  • 隐藏和重写方法
  • 继承链

继承的语法和基本概念

在C#中,使用:符号表示一个类继承自另一个类。例如,我们可以这样定义一个汽车类继承自交通工具类:

class Vehicle {
    // 交通工具类
}
class Car : Vehicle {
    // 汽车类继承自交通工具类
}

汽车类继承自交通工具类,那么汽车类就可以自动获得交通工具类中的所有属性和方法,无需重新定义。我们称交通工具类为父类或基类,汽车类为子类或派生类。

虚方法和抽象类

在父类中定义的方法,子类可以继承并使用。然而,在某些情况下,子类需要重写父类中的方法,例如:子类需要根据自己的特殊需求来重新实现某个方法。这就需要我们使用virtual关键字定义虚方法,例如:

class Vehicle {
    public virtual void Go() {
        Console.WriteLine("Vehicle is going.");
    }
}
class Car : Vehicle {
    public override void Go() {
        Console.WriteLine("Car is running.");
    }
}

在上面的代码中,我们定义了Vehicle类中的Go方法,使用了virtual关键字,表示这是一个虚方法,可以被子类重写。在Car类中,我们使用了override关键字来重写Go方法,并实现了自己的功能。

抽象类是父类中的一种特殊形式,它不能被直接实例化,只能被继承。抽象类中可以包含抽象方法,抽象方法必须被子类重写,否则子类也必须是抽象类。例如:

abstract class Vehicle {
    public abstract void Go();
}
class Car : Vehicle {
    public override void Go() {
        Console.WriteLine("Car is running.");
    }
}

接口继承

除了类继承,C#还提供了接口继承。一个接口定义了一组属性、方法和事件,但不包含实现。一个类可以实现一个或多个接口,以声明自己支持这些接口所定义的功能。例如:

interface IVehicle {
    void Go();
}
class Car : IVehicle {
    public void Go() {
        Console.WriteLine("Car is running.");
    }
}

上面的代码中,我们定义了一个IVehicle接口,它有一个Go方法。Car类实现了IVehicle接口,并实现了Go方法。

隐藏和重写方法

在C#中,派生类可以定义与基类同名的方法,这就涉及到隐藏和重写的概念。如果在派生类中定义了一个方法与基类中同名的方法,那么该方法就会隐藏基类中的方法。如果要调用基类中的方法,可以使用base关键字来指定。例如:

class Vehicle {
    public virtual void Go() {
        Console.WriteLine("Vehicle is going.");
    }
}
class Car : Vehicle {
    public new void Go() {
        Console.WriteLine("Car is running.");
    }
    public void GoBase() {
        // 调用基类的Go方法
        base.Go();
    }
}

在上面的代码中,我们定义了Car类的Go方法,它与基类中的Go方法同名。使用new关键字表示我们要隐藏基类中的方法。在Car类中,我们还定义了一个GoBase方法来调用基类的Go方法。

如果希望在派生类中重写基类的方法,需要使用override关键字。例如:

class Vehicle {
    public virtual void Go() {
        Console.WriteLine("Vehicle is going.");
    }
}
class Car : Vehicle {
    public override void Go() {
        Console.WriteLine("Car is running.");
        // 调用基类的Go方法
        base.Go();
    }
}

在上面的代码中,我们使用override关键字来重写基类的Go方法,并在子类中添加自己的实现。

继承链

在C#中,继承可以是多层嵌套的。例如:

class Vehicle {
    public virtual void Go() {
        Console.WriteLine("Vehicle is going.");
    }
}
class Car : Vehicle {
    public override void Go() {
        Console.WriteLine("Car is running.");
        base.Go();
    }
}
class Mustang : Car {
    public new void Go() {
        Console.WriteLine("Mustang is running.");
        base.Go();
    }
}

在上面的代码中,Mustang类继承自Car类,而Car类又继承自Vehicle类。因此,Mustang类不仅可以使用自己的方法,还可以使用父类和祖先类中的方法。必要时,还可以使用base关键字来访问特定层次的父类方法。

示例1:基类与派生类的方法重载

下面的代码演示了在基类和派生类中实现方法重载的方式:

using System;

namespace Demo1
{
    class Program
    {
        static void Main(string[] args)
        {
            // 创建一个Animal对象
            Animal animal1 = new Animal();
            // 调用Animal中的eat方法
            animal1.eat("grass");

            Console.WriteLine();

            // 创建一个Dog对象
            Dog dog1 = new Dog();
            // 调用Animal中的eat方法
            dog1.eat("bone");
            // 调用Dog中的eat方法
            dog1.eat("bone", 2);

            Console.ReadKey();
        }
    }

    class Animal
    {
        public void eat(string food)
        {
            Console.WriteLine("Animal is eating " + food);
        }

        public virtual void eat(string food, int count)
        {
            for (int i = 0; i < count; i++)
            {
                eat(food);
            }
        }
    }

    class Dog : Animal
    {
        public void eat(string food)
        {
            Console.WriteLine("Dog is eating " + food);
        }
    }

}

输出:

Animal is eating grass

Animal is eating bone
Dog is eating bone

在上面的代码中,我们定义了一个Animal类和一个Dog类。在Animal类中,我们定义了一个名为eat的方法,它接受一个参数food,输出一个信息。在Dog类中,我们也定义了一个名为eat的方法,但它与Animal类中的eat方法参数不同。我们还为Animal类和Dog类中的eat方法都添加了一个重载版本,其中Animal类中的版本是虚方法。

我们通过创建Animal和Dog对象来演示这些方法的使用。在Dog对象上调用eat方法时,如果传入一个参数,将会调用Dog类中的eat方法;如果传入两个参数,将会调用Animal类的虚方法。

示例2:多级继承示例

下面的代码演示了多级继承的示例,其中将定义三个类:

using System;

namespace Demo2
{
    class Program
    {
        static void Main(string[] args)
        {
            Employee emp1 = new Employee("Jack", "Xu", 2000);
            Manager mgr1 = new Manager("Bob", "Lee", 5000, "IT");
            Director dir1 = new Director("Amy", "Zhang", 10000, "Higher Education");

            emp1.print();
            mgr1.print();
            dir1.print();

            Console.ReadKey();
        }
    }

    class Employee
    {
        private string firstName;
        private string lastName;
        private decimal salary;

        public Employee(string fName, string lName, decimal sal)
        {
            firstName = fName;
            lastName = lName;
            salary = sal;
        }

        public void print()
        {
            Console.WriteLine("Employee Information: " + firstName + " " + lastName + ", Salary: " + salary);
        }
    }

    class Manager : Employee
    {
        private string department;

        public Manager(string fName, string lName, decimal sal, string dept) :
            base(fName, lName, sal)
        {
            department = dept;
        }

        public new void print()
        {
            Console.WriteLine("Manager Information: " + firstName + " " + lastName + ", Salary: " + salary + ", Department: " + department);
        }
    }

    class Director : Manager
    {
        private string institution;

        public Director(string fName, string lName, decimal sal, string dept) :
            base(fName, lName, sal, dept)
        {
            institution = "Our Institution";
        }

        public override void print()
        {
            Console.WriteLine("Director Information: " + firstName + " " + lastName + ", Salary: " + salary + ", Department: " + department + ", Institution: " + institution);
        }
    }
}

输出:

Employee Information: Jack Xu, Salary: 2000
Manager Information: Jack Xu, Salary: 5000, Department: IT
Director Information: Amy Zhang, Salary: 10000, Department: Higher Education, Institution: Our Institution

在上面的代码中,我们定义了三个类:Employee、Manager和Director。每个类都继承自前一个类,并添加了自己的成员。在Main方法中,我们创建了一个Employee、一个Manager和一个Director对象,分别调用它们的print方法。

我们可以看到,Employee类和Manager类中都定义了print方法,但Manager类中的版本是新方法。Director类重写了Manager类中的print方法,使用了override关键字。在Main方法中,我们创建了三个对象,并调用它们的print方法。在Director对象上调用print方法时,它将执行Director类中的版本,而不是Manager类中的版本。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:深入理解C#之继承 - Python技术站

(0)
上一篇 2023年5月15日
下一篇 2023年5月15日

相关文章

  • ASP.NET常用小技巧

    ASP.NET常用小技巧攻略 简介 ASP.NET是一个非常强大的Web框架,拥有很多小技巧可以节省开发时间并提高效率。本攻略将介绍一些ASP.NET的常用小技巧,可以帮助开发者更好的应用这个框架进行Web应用程序开发。 1. 锐利如刀的@ 符号 在ASP.NET中,@符号有多个用途,最常见的就是作为内嵌代码中的语句分隔符,但是他还有一些更强大的用途。 1.…

    C# 2023年6月3日
    00
  • c# 屏蔽快捷键的实现示例

    接下来我将详细讲解“C# 屏蔽快捷键的实现示例”的完整攻略,包含两个示例说明: 1. 使用WinAPI的方式屏蔽快捷键 1.1 前置知识 使用 C# 中屏蔽快捷键需要了解很多底层 Windows API 函数的使用方法,其中包括: SetWindowsHookEx 函数:用于钩取键盘操作 UnhookWindowsHookEx 函数:用于取消键盘钩子 Cal…

    C# 2023年6月7日
    00
  • 在Bootstrap开发框架中使用dataTable直接录入表格行数据的方法

    在Bootstrap开发框架中使用dataTable直接录入表格行数据的方法,主要分为两步: 引入dataTable插件 将数据添加到表格 以下为详细步骤: 1. 引入dataTable插件 首先在网页中引入jQuery库和dataTable插件。可以选择从官方网站下载,也可以通过CDN方式引入。 <!– 引入jQuery库 –> <s…

    C# 2023年5月31日
    00
  • C# IFF图形结构解析代码

    下面我将为您详细讲解“C# IFF图形结构解析代码”的完整攻略,包括基本概念、示例代码和应用方法。 什么是IFF图形结构? IFF全称为Interchange File Format,是一种通用的文件格式,广泛用于图像、声音、动画、视频等文件的交换和存储。IFF格式的文件通常由块(Chunk)构成,每个块由标识、长度和数据三部分组成。其中标识(FOURCC)…

    C# 2023年6月1日
    00
  • 如何在C#中使用注册表

    当我们需要在程序中保存一些配置信息,或者获取系统设置时,可以使用注册表来存储和读取这些信息。在C#中,我们可以利用Microsoft.Win32命名空间提供的类来操作注册表。 1. 引用命名空间 在使用注册表之前,首先需要引用Microsoft.Win32命名空间。可以在文件头部使用using语句引入命名空间: using Microsoft.Win32; …

    C# 2023年5月31日
    00
  • 初步认识C#中的Lambda表达式和匿名方法

    初步认识C#中的Lambda表达式和匿名方法 Lambda表达式 Lambda表达式是C# 3.0版本引入的新特性,可以看作是一个匿名函数,它可以传递到某些方法中,例如集合(List, Array)的Where() 方法。Lambda表达式允许您以更简洁的语法编写方法,从而使代码更简单易读。 Lambda表达式的语法格式为:(parameters) =&gt…

    C# 2023年6月1日
    00
  • C# ThreadPool之QueueUserWorkItem使用案例详解

    C# ThreadPool之QueueUserWorkItem使用案例详解 这篇文章介绍了C#中的线程池,及其使用方式之一:QueueUserWorkItem方法。接下来,我会更详细地讲解这篇文章的重点内容,以及为何可以使用它来实现线程池。 什么是线程池? 在线程池中,管理器维护多个已经创建的线程,使每个线程可以被重复利用,从而达到节省线程创建时间的目的,提…

    C# 2023年6月6日
    00
  • c# WPF设置软件界面背景为MediaElement并播放视频

    下面我将为你详细讲解“c# WPF设置软件界面背景为MediaElement并播放视频”的完整攻略。 1. 设置MediaElement控件作为背景 在WPF的XAML中,我们可以使用一个Grid来作为整个窗口的容器,而MediaElement则可以作为Grid的子元素。我们只需要将Grid的背景设置为Transparent即可将MediaElement设置…

    C# 2023年6月3日
    00
合作推广
合作推广
分享本页
返回顶部