深入浅出OOP(二): 多态和继承

news/2024/7/7 9:18:46 标签: c#, c/c++, python

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

本文是深入浅出OOP第二篇,主要说说继承的话题。

 

继承的介绍

在OOP中,继承有如下的定义:

  • 继承是一种OOP的机制,用于派生继承预定义的类

  • 在这个继承关系中,预定义的类是基类,新类是子类

  • 继承常常用于实现代码重用

  • 继承允许子类复用基类非private的的数据和方法

继承的实现

创建一个Console工程,命名为InheritanceAndPolymorphism。添加ClassA、ClassB类,并拷贝下面的代码:

ClassA:   class ClassA
     {
        
     }

ClassB:    class ClassB
    {        public int x = 100;        public void Display1()
        {
            Console.WriteLine("ClassB Display1");
        }        public void Display2()
        {
            Console.WriteLine("ClassB Display2");
        }
    }

 

在Program.cs中,调用ClassA

class Program
    {        static void Main(string[] args)
        {

            ClassA a = new ClassA();
            a.Display1();
        }
    }

如果运行,肯定会报错的。

Error: 'InheritanceAndPolymorphism.ClassA' does not contain a definition for 'Display1' and no extension method 'Display1' accepting a first argument of type 'InheritanceAndPolymorphism.ClassA' could be found

因为我们在ClassA中未定义Display1的方法。 下面我们重写,使ClassA继承自ClassB。

ClassA:  class ClassA:ClassB
    {
        
    }

ClassB:class ClassB
    {        public int x = 100;        public void Display1()
        {
            Console.WriteLine("ClassB Display1");
        }        public void Display2()
        {
            Console.WriteLine("ClassB Display2");
        }
    }

 

再次运行,结果如下:

ClassB Display1

ClassA已经可以访问其基类的Display1函数了,这个简单的实例说明了继承可复用基类的妙处,下面这张图以父子财产继承关系说明了继承的意义

image

 

再来看另外一个场景,假设ClassA也有一个Display1函数,签名和其基类一样的:

class ClassA:ClassB
    {        public void Display1()
        {
            System.Console.WriteLine("ClassA Display1");
        }
    }

ClassB:class ClassB
    {        public int x = 100;        public void Display1()
        {
            Console.WriteLine("ClassB Display1");
        }        public void Display2()
        {
            Console.WriteLine("ClassB Display2");
        }
    }

执行后结果如下:

ClassA Display1

看起来结果是对的,ClassA默认调用了自己的Display1函数,但是Visual Studio有一个警告:

Warning: 'InheritanceAndPolymorphism.ClassA.Display1()' hides inherited member 'InheritanceAndPolymorphism.ClassB.Display1()'. Use the new keyword if hiding was intended.

C#中对方法的调用首先是查询ClassA自己中有无Display1函数,再查询其基类有无Display1函数。在基类和子类出现同样函数的情况现实项目中是存在的,可能是基类代码过于陈旧了,子类既想用同签名的函数,又无法停止基类的同签名函数,故会出现这样的警告---尽管逻辑正确,但是这种设计还是有一些瑕疵的。

 

我们再试试在CalssA中通过base调用基类同名方法的情况:

ClassA:  class ClassA:ClassB
    {        public void Display1()
        {
            Console.WriteLine("ClassA Display1");            base.Display1();
        }
    }

ClassB:class ClassB
    {        public int x = 100;        public void Display1()
        {
            Console.WriteLine("ClassB Display1");
        }        public void Display2()
        {
            Console.WriteLine("ClassB Display2");
        }
    }

 

执行结果如下:

ClassA Display1

ClassB Display1

这个实验说明C#提供了base关键词,用于在继承中子类调用基类的函数或者变量(非private类型)。

 

image

 

同样的,在ClassA.Display1中调用其基类的Display2也是可以的,代码如下所示:

/// <summary>
   /// ClassB: acting as base class 
   /// </summary>
   class ClassB
    {        public int x = 100;        public void Display1()
        {
            Console.WriteLine("ClassB Display1");
        }        public void Display2()
        {
            Console.WriteLine("ClassB Display2");
        }
    }    /// <summary>
    /// ClassA: acting as derived class    /// </summary>
    class ClassA : ClassB
    {        public void Display1()
        {
            Console.WriteLine("ClassA Display1");            base.Display2();
        }
    }    /// <summary>
    /// Program: used to execute the method.    /// Contains Main method.    /// </summary>
    class Program
    {        static void Main(string[] args)
        {
            ClassA a = new ClassA();
            a.Display1();
            Console.ReadKey();
        }
    }

 

执行结果如下:

ClassA Display1

ClassB Display2

 

那么可否通过基类调用其子类的函数呢?

/// <summary>
   /// ClassB: acting as base class 
   /// </summary>
   class ClassB
    {        public int x = 100;        public void Display1()
        {
            Console.WriteLine("ClassB Display1");
        }
    }    /// <summary>
    /// ClassA: acting as derived class    /// </summary>
    class ClassA : ClassB
    {        public void Display2()
        {
            Console.WriteLine("ClassA Display2");
        }
    }    /// <summary>
    /// Program: used to execute the method.    /// Contains Main method.    /// </summary>
    class Program
    {        static void Main(string[] args)
        {
            ClassB b = new ClassB();
            b.Display2();
            Console.ReadKey();
        }
    }

 

运行报错:

Error: 'InheritanceAndPolymorphism.ClassB' does not contain a definition for 'Display2' and no extension method 'Display2' accepting a first argument of type 'InheritanceAndPolymorphism.ClassB' could be found

原因是继承无法实现逆向调用,既基类无法调用子类。

除了构造函数和析构函数,子类继承了其基类的一些(包括private的成员变量和成员函数,只是无法访问)。

在C#中,一个类默认继承的是object类型,object是C#所有引用类型的基类;同时,继承具有传递性,如ClassC继承自ClassB,ClassB继承自ClassA,则ClassC可完全复用ClassA的数据和函数---ClassC继承了ClassA。

C#中所有的类型都可被继承吗?

public class ClassW : System.ValueType
   {
   }   public class ClassX : System.Enum
   {
   }   public class ClassY : System.Delegate
   {
   }   public class ClassZ : System.Array
   {
   }

执行结果:

'InheritanceAndPolymorphism.ClassW' cannot derive from special class 'System.ValueType'

'InheritanceAndPolymorphism.ClassX' cannot derive from special class 'System.Enum'

'InheritanceAndPolymorphism.ClassY' cannot derive from special class 'System.Delegate'

'InheritanceAndPolymorphism.ClassZ' cannot derive from special class 'System.Array'

 

运行的结果让人抓狂

image

在C#中,自定义类无法继承自C#内置的一些类,如System.ValueTypeSystem.EnumSystem.Delegate,System.Array, etc。

 

下面这个例子我们再看看C++中的多类继承是否可在C#中实现:

    public class ClassW
    {
    }    public class ClassX
    {
    }    public class ClassY : ClassW, ClassX
    {
    }

执行结果:

Compile time Error: Class 'InheritanceAndPolymorphism.ClassY' cannot have multiple base classes: 'InheritanceAndPolymorphism.ClassW' and 'ClassX'.

 

执行结论是:C#仅支持单类继承,不支持C++的这种星型继承关系。 要使用星型继承关系,请用接口实现。

 

那么可否实现循环依赖继承呢?

   public class ClassW: ClassY
    {
    }    public class ClassX: ClassW
    {
    }    public class ClassY :  ClassX
    {
    }

 

代码逻辑很简单,ClassW继承自ClassY,ClassX继承自ClassW, ClassY继承自ClassX。

但是编译后报错了:

Error: Circular base class dependency involving 'InheritanceAndPolymorphism.ClassX' and 'InheritanceAndPolymorphism.ClassW'.

image

 

我们得出一个结论,C#中不许环形依赖继承。

 

实例对象的是否可赋值

ClassB:public class ClassB
    {        public int b = 100;
    }

ClassA:    public class ClassA
    {        public int a = 100;
    }

 

Program.cs 代码如下

/// <summary>
    /// Program: used to execute the method.    /// Contains Main method.    /// </summary>
    public class Program
    {        private static void Main(string[] args)
        {
            ClassB classB = new ClassB();
            ClassA classA = new ClassA();
            classA = classB;
            classB = classA;
        }
    }

 

我们尝试判断ClassA、ClassB的对象是否可赋值。

编译的结果是:报错了

Cannot implicitly convert type 'InheritanceAndPolymorphism.ClassB' to 'InheritanceAndPolymorphism.ClassA' Cannot implicitly convert type 'InheritanceAndPolymorphism.ClassA' to 'InheritanceAndPolymorphism.ClassB'

image

尽管ClassA和ClassB里面的数据成员变量a数据一致,都为100,但是这里用等号比较的是类型--引用地址,故无法进行赋值。

 

我们再来试试继承关系的:

public class ClassB
    {        public int b = 100;
    }    public class ClassA:ClassB
    {        public int a = 100;
    }    /// <summary>
    /// Program: used to execute the method.    /// Contains Main method.    /// </summary>
    public class Program
    {        private static void Main(string[] args)
        {
            ClassB classB = new ClassB();
            ClassA classA = new ClassA();
            classA = classB;
            classB = classA;
        }
    }

 

ClassA继承自ClassB,我们希望可以直接赋值其实例对象。

 

运行结果如下:

Error: Cannot implicitly convert type 'InheritanceAndPolymorphism.ClassB' to 'InheritanceAndPolymorphism.ClassA'.

运行结论:C#中子类对象可直接赋值给基类对象,基类对象需要往下强转。代码修改如下:

public class ClassB
    {        public int b = 100;
    }    public class ClassA:ClassB
    {        public int a = 100;
    }    /// <summary>
    /// Program: used to execute the method.    /// Contains Main method.    /// </summary>
    public class Program
    {        private static void Main(string[] args)
        {
            ClassB classB = new ClassB();
            ClassA classA = new ClassA();
            classB=classA;
            classA = (ClassA)classB;
        }
    }

 

这样编译就通过了。

如果ClassA不继承自ClassB,则这种强转在C#中是会报错的:

Cannot convert type 'InheritanceAndPolymorphism.ClassA' to 'InheritanceAndPolymorphism.ClassB'

Cannot convert type 'InheritanceAndPolymorphism.ClassB' to 'InheritanceAndPolymorphism.ClassA'

 

 

本节结论

  • 无法阻止子类覆盖基类同签名方法

  • 继承关系是子类的同签名方法先查找,再查找其基类的

  • base关键字被C#用于在子类中调用基类函数、变量

  • 继承关系不可逆转

  • 除了构造函数、析构函数,子类继承了基类的一些

  • 自定义类默认继承自Object类型,但是C#的这些类型不能被继承:System.ValueTypeSystem.Enum,System.DelegateSystem.Array, etc.

  • C#不支持从多类继承

  • C#不支持循环继承

  • 子类对象可直接赋值给基类,反之需要强转

 

登录乐搏学院官网http://www.learnbo.com/

或关注我们的官方微博微信,还有更多惊喜哦~

原文地址:Diving in OOP (Day 2): Polymorphism and Inheritance (Inheritance)

转载于:https://my.oschina.net/learnbo/blog/820072


http://www.niftyadmin.cn/n/634834.html

相关文章

【分享】最新版PLSQL下载地址

官方下载地址&#xff1a; http://download.allroundautomations.com/plsqldev1100.exe 注册码&#xff1a; Product Code&#xff1a;4t46t6vydkvsxekkvf3fjnpzy5wbuhphqz serial Number&#xff1a;601769 password&#xff1a;xs374ca

touch 命令

touch命令有两个功能&#xff1a;一是用于把已存在文件的时间标签更新为系统当前的时间&#xff08;默认方式&#xff09;&#xff0c;它们的数据将原封不动地保留下来&#xff1b;二是用来创建新的空文件。语法touch &#xff08;选项) (参数)选项-a&#xff1a;或--timeatime…

vector中insert()的用法详解

insert() 函数有以下三种用法: 1、在指定位置loc前插入值为val的元素,返回指向这个元素的迭代器 2、在指定位置loc前插入num个值为val的元素 3、在指定位置loc前插入区间[start, end)的所有元素 举例: //创建一个vector,置入字母表的前十个字符 vector <char> Av…

手把手教你DIY一个春运迁徙图(一)

换了新工作&#xff0c;也确定了我未来数据可视化的发展方向。新年第一篇博客&#xff0c;又逢春运&#xff0c;这篇技术文章就来交给大家如何做一个酷炫的迁徙图&#xff08;支持移动哦&#xff09;。&#xff08;求star 代码点这里&#xff09; 迁徙图的制作思路分为静态的元…

Oracle 日期格式化处理汇总

一、 日期及时间格式化应用TO_CHAR(日期&#xff0c;格式化参数) 1、返回任意有效分割符拼接的年月日字符串 <pre name"code" class"sql"><span style"font-size:18px;">1.1、Select to_char(sysdate,yyyy/mm/dd) From dual; *****…

哈希表(散列表)原理详解

什么是哈希表&#xff1f; 哈希表&#xff08;Hash table&#xff0c;也叫散列表&#xff09;&#xff0c;是根据关键码值(Key value)而直接进行访问的数据结构。也就是说&#xff0c;它通过把关键码值映射到表中一个位置来访问记录&#xff0c;以加快查找的速度。这个映射函数…

Mysql事务-隔离级别

MYSQL事务-隔离级别 事务是什么? 事务简言之就是一组SQL执行要么全部成功&#xff0c;要么全部失败。MYSQL的事务在存储引擎层实现。 事务都有ACID特性&#xff1a; 原子性&#xff08;Atomicity&#xff09;&#xff1a;一个事务必须被视为一个不可分割的单元&#xff1b; 一…

Top K 算法详解(哈希表Hash的使用)

要说明这个问题&#xff0c;还是用实例说明比较容易理解&#xff0c;看下面一道百度面试题&#xff1a; 搜索引擎会通过日志文件把用户每次检索使用的所有检索串都记录下来&#xff0c;每个查询串的长度为1-255字节。 假设目前有一千万个记录&#xff08;这些查询串的重复度…