【C#小知识】C#中一些易混淆概念总结(五)---------深入解析C#继承

简介:

目录:

【C#小知识】C#中一些易混淆概念总结--------数据类型存储位置,方法调用,out和ref参数的使用

【C#小知识】C#中一些易混淆概念总结(二)--------构造函数,this关键字,部分类,枚举

【C#小知识】C#中一些易混淆概念总结(三)--------结构,GC回收,静态成员,静态类

【C#小知识】C#中一些易混淆概念总结(四)---------解析Console.WriteLine()

----------------------------------分割线--------------------------------------

这次主要分享的内容是关于继承的知识。

首先,我们先来看看继承;

既然有继承,就要有父类和子类,来看下面的一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class  Person
     {
         private  int  nAge;
         protected  string  strName;
         double  douHeight;
         public  string  strEateType;
         public  void  Hello()
         {
             Console.WriteLine( "我可以说Hello!" );
         }
         public  void  Run()
         {
             Console.WriteLine( "我可以跑!" );
         }
     }
     class  Student : Person
     {
     }

然后我在Main()函数中实例化子类的对象,代码如下:

staticvoid Main(string[] args)        

{        

   Student stu1 new Student();    

  }

那么在这个过程中内存中发生了些什么呢?

我们先来看misl的中间代码,看看那能发现些什么


061458426044696.png

由此我们可以发现子类继承了父类的所有成员包括Private和Protect,并为这些成员开辟了空间来存储。

我们再来实例化我们的子类,然后访问父类的字段和方法,会发现,如下的现象

061505509659962.png

所以虽然子类为父类的所有成员在堆中都开辟了空间,但是父类的私有成员(Private)子类访问不到,

而受保护的成员(protected)可以通过实例化对象访问的到。


所以在内存中的情况如下图:

061525055993918.png

看下面的代码,我们来探究一下在子类中this关键字和base关键字所访问的类的成员有哪些,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class  Student : Person
     {
         private  string  strClass;
         private  string  strAddress;
         public  void  Address( string  cla,  string  adre)
         {
             //这里的this关键字调用了子类的成员和父类的非似有成员
             this .strClass =  "五" ;
             this .strAddress =  "北京" ;
             this .strName =  "子强" ;
             //这里base关键字调用了是父类的非似有成员
             base .strName =  "强子" ;
             Console.WriteLine( "我是{0}年纪,来自{1}" , cla, adre);
         }
         public  void  Sing()
         {
             this .strClass =  "" ;
             Console.WriteLine( "我可以唱歌!" );
         }
     }

所以在子类中this关键字和base关键字的访问范围的示意图如下:

061715362894801.png

二,关于子类对象的构造函数和父类构造函数的执行顺序

我们分别为父类和子类添加显式的构造函数,代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class  Person
     {
         private  int  nAge;
         protected  string  strName;
         double  douHeight;
         public  string  strEateType;
         //父类的构造函数
         public  Person()
         {
             Console.WriteLine( "我是父类的构造函数" );
         }
         public  void  Hello()
         {
             Console.WriteLine( "我可以说Hello!" );
         }
         public  void  Run()
         {
             Console.WriteLine( "我可以跑!" );
         }
     }
     class  Student : Person
     {
         private  string  strClass;
         private  string  strAddress;
         //子类的构造函数
         public  Student ()
         {
             Console.WriteLine( "我是子类的构造函数" );
         }
     }

我们使用VS的单步调试,来看父类和子类显式构造函数的执行顺序,如下图(动态图片,可以看到过程):

061724588334412.gif

很容易的可以发现,当创建子类对象的时候

①先调用了子类的构造函数

②调用了父类的构造函数

③执行了父类的构造函数

④执行了子类的构造函数


那么为什么会这样呢?

我尝试通过反编译看源码来解释这个原因,但是反编译的结果如下,

061801203361346.png

没有发现有什么特别的地方可以解释这个原因。


最后还是查阅微软的MSDN官方文档找到了答案(原文地址点击这里

061801068235143.png

根据微软官方的代码示例,那么下面的代码的效果也是相同的

1
2
3
4
5
6
7
8
9
10
11
//子类的构造函数
         public  Student ()
         {
             Console.WriteLine( "我是子类的构造函数" );
         }
//这里的代码和上面的代码效果是相同的
         public  Student()
             : base ()
         {
             Console.WriteLine( "我是子类的构造函数" );
         }

也就是说只要在子类显式的声明了无参的构造函数,在实例化子类的对象是,子类的无参构造函数都会去调用父类无参的构造函数。

那么,如果父类没有这个无参的构造函数则会报错。

如下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class  Person
     {
         private  int  nAge;
         protected  string  strName;
         double  douHeight;
         public  string  strEateType;
         //父类的构造函数
         //public Person()
         //{
         //    Console.WriteLine("我是父类的构造函数");
         //}
       //父类的有参数的构造函数,这里覆盖了无参的构造函数
         public  Person ( string  str)
         {
             Console.WriteLine( "我是父类的构造函数{0}" ,str);
         }
         public  void  Hello()
         {
             Console.WriteLine( "我可以说Hello!" );
         }
         public  void  Run()
         {
             Console.WriteLine( "我可以跑!" );
         }
     }
     class  Student : Person
     {
         private  string  strClass;
         private  string  strAddress;
         //子类的无参构造函数
         public  Student ()
         {
             Console.WriteLine( "我是子类的构造函数" );
         }
         public  Student( string  strName)
         {
             Console.WriteLine( "我的名字叫{0}" ,strName);
         }
     }


这时候编译会报错,

061816033476317.png

因为在父类中有参数的构造函数覆盖了无参数的构造函数,所以在子类的无参数的构造函数没办法回调父类的无参数的构造函数初始化父类的成员变量。所以报错。

那么在初始化子类的时候,为什么要调用父类的构造函数呢?

在初始化子类之前需要通过构造函数初始化父类的成员变量

父类的构造函数先于子类的构造函数执行的意义是什么呢?

当在父类的构造函数中和子类的构造函数中为父类的非私有成员变量赋不同默认值。当实例化子类,子类要调用构造函数初始化成员变量,如果先执行了子类的构造函数,再执行父类的构造函数,父类成员字段的值会覆盖子类成员字段的值。但是我们想得到的是子类的属性值。所以为了解决数据冲突,父类的构造函数要先于子类的构造函数执行。

如下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class  Person
     {
         private  int  nAge;
         private  string  strName;
         double  douHeight;
         public  string  strEateType;
        // 父类的构造函数
         public  Person()
         {
             //再父类中对strEateType赋初始值
             this .strEateType =  "吃饭" ;
             Console.WriteLine( "我是父类的构造函数{0}" , strEateType);
         }
     }
     class  Student : Person
     {
         private  string  strClass;
         private  string  strAddress;
         //子类的构造函数
         public  Student()
         {
             //在子类中对strEateType赋初始值
             this .strEateType =  "吃面条" ;
             Console.WriteLine( "我是子类的构造函数{0}" ,strEateType);
         }
     }

这时候我们通过,声明子类对象访问strEateType的值,如下:

1
2
3
4
5
Student stu1 =  new  Student();
             //stu1.
             string  str = stu1.strEateType.ToString();
             Console.WriteLine(str);
             Console.ReadKey();

这里肯定是要打印出子类的属性strEateType的值,如果先执行子类构造函数对strEateType赋值,然后父类的构造函数赋值覆盖strEateType的初始值。那么打印出的将是父类成员字段的值。所以,父类的构造函数先于子类的构造函数执行。

打印结果如下:

061910383266926.png


三,子类是否可以有和父类的同名方法

看下面的代码,我们声明一个父类Person:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class  Person
     {
         private  int  nAge;
         private  string  strName;
         double  douHeight;
         public  string  strEateType;
        public   readonly  string  strrrr;
         // 父类的构造函数
         public  Person()
         {
             this .strEateType =  "吃饭" ;
             Console.WriteLine( "我是父类的构造函数{0}" , strEateType);
         }
         public  Person( string  str)
         {
             this .strName = str;
             Console.WriteLine( "我是父类的构造函数{0}" , str);
         }
         public  void  Hello()
         {
             Console.WriteLine( "我可以说地球人的Hello!" );
         }
         public  void  Run()
         {
             Console.WriteLine( "我可以跑!" );
         }
     }


声明一个子类继承Person,代码如下:

1
2
3
4
5
6
7
8
9
10
11
class  Worker:Person
     {
         public  void   Hello()
         {
             Console.WriteLine( "我是工人会说Hello!" );
         }
         public  new  void   Run()
         {
             Console.WriteLine( "我是工人我会奔跑!" );
         }
     }


然后实例化Worker对象,打印Hello方法,结果如下图:

062124112658094.png


这是为什么呢?编译器已经告诉了我们,如下图:

062126425532539.png


看出来是子类的方法隐藏了父类的方法。

既然子类可以定义和父类同名的方法,那么是否可以定同名的字段呢?答案是肯定的,而且会像同名方法一样,子类同名字段会隐藏父类同名的字段










     本文转自yisuowushinian 51CTO博客,原文链接:http://blog.51cto.com/yisuowushinian/1356975,如需转载请自行联系原作者





相关文章
|
8月前
|
存储 算法 安全
如何控制上网行为——基于 C# 实现布隆过滤器算法的上网行为管控策略研究与实践解析
在数字化办公生态系统中,企业对员工网络行为的精细化管理已成为保障网络安全、提升组织效能的核心命题。如何在有效防范恶意网站访问、数据泄露风险的同时,避免过度管控对正常业务运作的负面影响,构成了企业网络安全领域的重要研究方向。在此背景下,数据结构与算法作为底层技术支撑,其重要性愈发凸显。本文将以布隆过滤器算法为研究对象,基于 C# 编程语言开展理论分析与工程实践,系统探讨该算法在企业上网行为管理中的应用范式。
251 8
|
4月前
|
XML 前端开发 C#
C#编程实践:解析HTML文档并执行元素匹配
通过上述步骤,可以在C#中有效地解析HTML文档并执行元素匹配。HtmlAgilityPack提供了一个强大而灵活的工具集,可以处理各种HTML解析任务。
266 19
|
8月前
|
存储 监控 算法
解析公司屏幕监控软件中 C# 字典算法的数据管理效能与优化策略
数字化办公的时代背景下,企业为维护信息安全并提升管理效能,公司屏幕监控软件的应用日益普及。此软件犹如企业网络的 “数字卫士”,持续记录员工电脑屏幕的操作动态。然而,伴随数据量的持续增长,如何高效管理这些监控数据成为关键议题。C# 中的字典(Dictionary)数据结构,以其独特的键值对存储模式和高效的操作性能,为公司屏幕监控软件的数据管理提供了有力支持。下文将深入探究其原理与应用。
204 4
|
9月前
|
机器学习/深度学习 监控 算法
员工上网行为监控软件中基于滑动窗口的C#流量统计算法解析​
在数字化办公环境中,员工上网行为监控软件需要高效处理海量网络请求数据,同时实时识别异常行为(如高频访问非工作网站)。传统的时间序列统计方法因计算复杂度过高,难以满足低延迟需求。本文将介绍一种基于滑动窗口的C#统计算法,通过动态时间窗口管理,实现高效的行为模式分析与流量计数。
282 2
|
10月前
|
存储 JSON 数据格式
ElasticSearch基础概念解析
以上就是ElasticSearch的基础概念。理解了这些概念,你就可以更好地使用ElasticSearch,像使用超级放大镜一样,在数据海洋中找到你需要的珍珠。
312 71
|
10月前
|
监控 算法 安全
基于 C# 的内网行为管理软件入侵检测算法解析
当下数字化办公环境中,内网行为管理软件已成为企业维护网络安全、提高办公效率的关键工具。它宛如一位恪尽职守的网络守护者,持续监控内网中的各类活动,以确保数据安全及网络稳定。在其诸多功能实现的背后,先进的数据结构与算法发挥着至关重要的作用。本文将深入探究一种应用于内网行为管理软件的 C# 算法 —— 基于二叉搜索树的入侵检测算法,并借助具体代码例程予以解析。
167 4
|
12月前
|
XML Java 开发者
Spring底层架构核心概念解析
理解 Spring 框架的核心概念对于开发和维护 Spring 应用程序至关重要。IOC 和 AOP 是其两个关键特性,通过依赖注入和面向切面编程实现了高效的模块化和松耦合设计。Spring 容器管理着 Beans 的生命周期和配置,而核心模块为各种应用场景提供了丰富的功能支持。通过全面掌握这些核心概念,开发者可以更加高效地利用 Spring 框架开发企业级应用。
384 18
|
存储 安全 编译器
学懂C#编程:属性(Property)的概念定义及使用详解
通过深入理解和使用C#的属性,可以编写更清晰、简洁和高效的代码,为开发高质量的应用程序奠定基础。
1004 12
|
编译器 C# 开发者
C# 9.0 新特性解析
C# 9.0 是微软在2020年11月随.NET 5.0发布的重大更新,带来了一系列新特性和改进,如记录类型、初始化器增强、顶级语句、模式匹配增强、目标类型的新表达式、属性模式和空值处理操作符等,旨在提升开发效率和代码可读性。本文将详细介绍这些新特性,并提供代码示例和常见问题解答。
334 7
C# 9.0 新特性解析
|
存储 监控 算法
企业内网监控系统中基于哈希表的 C# 算法解析
在企业内网监控系统中,哈希表作为一种高效的数据结构,能够快速处理大量网络连接和用户操作记录,确保网络安全与效率。通过C#代码示例展示了如何使用哈希表存储和管理用户的登录时间、访问IP及操作行为等信息,实现快速的查找、插入和删除操作。哈希表的应用显著提升了系统的实时性和准确性,尽管存在哈希冲突等问题,但通过合理设计哈希函数和冲突解决策略,可以确保系统稳定运行,为企业提供有力的安全保障。

热门文章

最新文章

推荐镜像

更多
  • DNS