一名.NET程序员给我发了一封邮件,讨论C#2.0的对象模型:
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
金老师:
您好!
我是一名.net程序员。拜读了您的“.net 2.0面向对象编程揭秘”这本书,受益匪浅。
您的书写得非常深入,经常让我有恍然大悟的感觉。很多堆积已久的问题迎刃而解。同时我也有了很多疑问。希望您能帮我解答。
1.您说子类会调用父类的构造函数。同时“子类对象集成了基类的实例字段”(P213)。“基类的实例字段”包括父类的private字段么?
2.Son son=new Son();
Father father=son;
这是您介绍的多态编程。子类实例变量赋值给父类变量。
这样的两句代码,在内存中将会发生什么呢?
对象的实例没有改变么?faher和son存的是同一个首地址?
这时father能调用的都是Father类型的字段和方法。那么father的类型表指针应该指向Father类型表,这样可以解释father调用父类的方法。那么字段呢?
father is Son
这又是如何实现的呢?
3.值类型在编译期已经在栈上分配好了内存
编译期还是IL指令,还没有转化为cpu可执行的二进制代码。这时内存是如何分配好的?
编译期为什么要分配内存呢?不是应该运行的时候才需要么?
4.您的书中在说明IL代码的时候多次提及计算堆栈。
您能把IL代码执行的基本原理告诉我吗?或者从哪里可以看到相关的资料。
5.对象实例存储在堆中,那么对象变量呢?是存储在线程堆栈中么?
同时我也发现了您的一个疏漏。
第183页 第10行 首先调用父类构造函数,再调用子类构造函数。
第211页 第6行 在构造函数中,先初始化自身的字段,在调用基类的构造函数。
这两句表达有误。
应该是:
先调用子类构造函数,通过子类构造函数调用父类构造函数。先执行父类构造函数的代码,初始化父类的字段,再回到子类初始化子类的字段。
我编写了这样两个简单的类
public class Father
{
protected string a;
public Father()
{
a = "a";
}
}
public class Son : Father
{
private string b;
public Son()
{
b = a;
}
}
子类的构造函数IL代码如下:
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// 代码大小 17 (0x11)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void ConsoleApplication1.Father::.ctor()
IL_0006: nop
IL_0007: nop
IL_0008: ldarg.0
IL_0009: ldc.i4.1
IL_000a: stfld int32 ConsoleApplication1.Son::b
IL_000f: nop
IL_0010: ret
} // end of method Son::.ctor
这段IL代码好很的证明了我的观点。
希望您能耐心的解答我的问题。谢谢
++++++++++++++++++++++++++++++++++++++++++
我的回复如下:
-----------------------------
1 “基类的实例字段”包括父类的private字段,但子类方法不能存取这个字段,这是由编译器在生成IL指令时保证的。
2 Son son=new Son();
Father father=son;
实际上,在内存中只有一个Son对象实例(放在堆中),但存在两个类型表(由CLR直接管理,当卸掉程序集时,这些类型表占用的资源被回收),赋值后,father和son存的是同一个首地址,指向在托管堆中的Son对象实例。
father is son不是在程序运行时实现的,而是在编译时实现的,由C#编译器直接将这个代码翻译为IL指令,此IL指令会根据你的代码生成对合适方法的调用指令。
当程序运行时,CLR直接装入的是翻译好的IL指令,而非C#代码,IL指令本身是没有“多态”特性的,因为它已经比较靠近底层,应该尽可能地简化以提高效率。
3 值类型在编译期已经在栈上分配好了内存
这句是错的,变量的内存分配是在程序运行时才进行的。是我的疏漏。
4 CLR可以看成是一个基于堆栈的虚拟计算机,这台机器运行的是IL汇编程序,凡是IL代码中所说的堆栈,都是指“计算堆栈(Evaluation Stack)”。在书的附录中有一个“MSIL基础教程”,其中介绍了相关的原理。有关IL的资料很少,国内可以看到的书就是《Inside Microsoft .NET IL Assembly》,千万别看中文版,我看译者肯定没弄明白其中的技术内容,译得一踏糊涂。要看就看英文版,但这本书阅读难度很大,作者是技术牛人,但作为一名作家,我认为不合格。本书中有关IL编程的介绍是我经过收集相关资料进行消化,并经实践检验之后写的,但管中窥豹,仅供参考。
5.你说得对:对象实例的数据存储在托管堆中,引用此对象的对象变量则是存储在线程堆栈中。
关于子类字段与父类字段初始化顺序的问题,我注意到你在子类构造函数中使用了基类的数据成员,因此才会导致先初始化基类数据成员,后初始化子类数据成员。这是一种特例。
事实上,如果子类字段与父类字段没有这种依存关系,C#编译器是按照以下顺序生成IL指令的:
new 子类对象时,子类构造函数被调用,在执行子类构造函数的代码时,先初始化子类的字段,然后再调用父类的构造函数初始化父类的字段。
如果子类字段依赖于父类字段的值,C#编译器在生成IL指令时,会先调用父类的构造函数初始化父类的字段,再调用子类构造函数初始化依赖于父类字段的这些字段。
我修改了一下你的代码,给子类和父类增加两个独立的字段:
public class Father
{
protected string a;
public int fatherFld=100;
public Father()
{
a = "a";
}
}
public class Son : Father
{
private string b;
private int sonFld = 200;
public Son()
{
b = a;
}
}
子类生成的IL代码如下:
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// 代码大小 33 (0x21)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldc.i4 0xc8
IL_0006: stfld int32 ConsoleApplication1.Son::sonFld
IL_000b: ldarg.0
IL_000c: call instance void ConsoleApplication1.Father::.ctor()
IL_0011: nop
IL_0012: nop
IL_0013: ldarg.0
IL_0014: ldarg.0
IL_0015: ldfld string ConsoleApplication1.Father::a
IL_001a: stfld string ConsoleApplication1.Son::b
IL_001f: nop
IL_0020: ret
} // end of method Son::.ctor
可以看到IL_0006句先初始化子类的字段,IL_000c再调用父类的构造函数初始化父类字段,再回过头来于IL_0015和IL_001a两句完成用父类字段初始化子类字段的工作。
父类构造函数IL代码如下:
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// 代码大小 29 (0x1d)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldc.i4.s 100
IL_0003: stfld int32 ConsoleApplication1.Father::fatherFld
IL_0008: ldarg.0
IL_0009: call instance void [mscorlib]System.Object::.ctor()
IL_000e: nop
IL_000f: nop
IL_0010: ldarg.0
IL_0011: ldstr "a"
IL_0016: stfld string ConsoleApplication1.Father::a
IL_001b: nop
IL_001c: ret
} // end of method Father::.ctor
注意一下基类object构造函数的调用是插在两个字段初始化指令中间的。我经过实验发现,C#编译器生成IL代码时对于字串类型字段的初始化总是在调用基类构造函数之后,而象int之类的字段,如果是独立的,其初始化指令总在调用基类构造函数指令之前。
为什么这样,只好去问问C#编译器的设计者了。
----------------------------
欢迎就此问题进行讨论。
分享到:
相关推荐
《C# 2.0 宝典》是一本专注于C# 2.0编程语言的权威指南,书中深入探讨了C#语言的核心特性和高级应用。这本书提供了丰富的实例,包括两个经典的学生管理系统,一个基于控制台(Console Application,简称cs)和另一个...
《程序天下C#2.0实用开发参考大全》是一本专为C# 2.0开发者设计的详尽参考资料,旨在帮助程序员深入理解和熟练运用C#语言进行软件开发。本书全面覆盖了C# 2.0的核心概念、语法特性、编程技巧以及实际应用案例,旨在...
《C#2.0.锐利体验系列课程(4):杂项技术,以及C#语言的未来发展》 本课程是"C#2.0.锐利体验"系列的第四部分,主要聚焦于C# 2.0中的杂项技术,并探讨了C#语言的未来发展趋势。在这一章节中,我们将深入学习C# 2.0的...
在C# 2.0 代码生成器中,工厂模式被用来封装代码生成的流程,通过定义一个创建对象的接口,让子类决定实例化哪一个类。这样,代码生成器可以根据不同的输入参数或配置,动态地生成对应的代码片段。 三、代码生成器...
本文将深入探讨如何利用C# 2.0编程语言开发一个UML设计器,特别关注矢量图形编辑器的实现,以及序列化和反序列化矢量对象的关键技术。 首先,让我们了解UML的基础。UML是一种标准化的语言,通过图表来表达软件系统...
《C#2.0 锐利体验系列课程(4):杂项技术,以及C#语言的未来发展》 本课程是C#2.0学习系列的第四部分,主要聚焦于C#2.0中的杂项技术及其对未来C#语言发展的影响。在C#2.0的迭代中,微软引入了一系列新特性,旨在提升...
- 基于C# 2.0:C#是微软公司推出的面向对象的编程语言,2.0版本在1.0的基础上增加了泛型、匿名方法、迭代器等功能,为开发复杂的Web应用程序提供了更强大的支持。 - 使用.NET Framework:C#是.NET框架的一部分,该...
《网络五子棋(C#2.0)源码解析与技术探讨》 网络五子棋是一款基于C# 2.0编程语言开发的在线对战游戏,它利用.NET框架为玩家提供实时的对弈体验。在这款游戏中,两位玩家通过网络连接进行对局,共享棋盘状态,实现...
《友思-网络五子棋(C#2.0) 源码》是基于C# 2.0编程语言开发的一款在线对战的五子棋游戏。本项目旨在提供一个学习和研究网络交互、游戏逻辑以及图形用户界面设计的实例。下面我们将详细探讨这款源码中的关键知识点。...
《C# 2.0 宝典》是张立撰写的一本关于C#编程语言的权威指南,书中详细讲解了C# 2.0版本的各种特性和应用。这本书的源代码包含27个章节,每个章节都对应着特定的知识点和实例,旨在帮助读者深入理解和实践C#编程。...
本项目以“餐饮管理系统(C# 2.0+SQL Server 2000 实现)”为主题,深入探讨了如何利用C#编程语言和SQL Server 2000数据库技术来构建一个高效、易用的餐饮管理平台。此系统不仅能够提高餐厅的运营效率,还能为管理者...
C# 2.0是微软公司推出的一种面向对象的编程语言,具有语法简洁、类型安全、性能高效的特点。在家庭视频监控系统中,C# 2.0用于编写用户界面、处理视频流、控制视频采集卡等功能。开发者可以利用.NET Framework提供的...
C# 2.0是.NET Framework的一个重要组成部分,它提供了一种强大的面向对象的编程模型,支持类型安全、垃圾回收、自动内存管理等功能,使得开发人员能够更高效地构建Windows桌面应用。在卡拉OK点歌系统中,C# 2.0被...
《Windows Forms Programming with C# 2.0编程》是一本深度探讨WinForm 2.0开发的书籍,专为C#开发者设计。Windows Forms是.NET Framework的重要组成部分,它提供了丰富的用户界面(UI)控件和事件处理机制,使得...
12. **多线程与并发**:C# 2.0提供了`System.Threading`命名空间,支持线程创建、同步机制(如Monitor, Mutex, Semaphore)以及异步编程模型。 13. **内存管理与垃圾回收**:C#的内存管理由.NET框架自动进行,通过...
本篇将深入探讨C#语言规范的20版,旨在为开发者提供详尽的理解和指导。 首先,C# 2.0是一个重要的里程碑,它在C# 1.2的基础上引入了多项新特性。其中最显著的是对泛型的支持,泛型允许程序员创建可重用的数据结构和...
本文将深入探讨基于C# .NET 2.0 开发的域名空间网站系统,旨在为开发者提供一种参考性的学习路径。 首先,我们要理解C# .NET 2.0 的核心优势。C# 是一种面向对象的编程语言,具有类型安全、垃圾回收、异常处理等...
2. **C#编程**:讲解C#语法基础,如变量、数据类型、流程控制语句、类和对象等,为使用ASP.NET 2.0开发奠定坚实的语言基础。 3. **Web Forms**:深入探讨ASP.NET Web Forms,包括控件间的交互、状态管理、验证控件...
《Professional C# 7 and .NET Core 2.0》这本书深入探讨了C#编程语言的最新版本(C# 7)以及.NET Core 2.0框架,为开发者提供了全面而详细的学习资源。C#是一种面向对象的编程语言,由微软开发并广泛应用于Windows...
《C#和.NET 2.0实战:平台、语言与框架》是一本深入探讨C#编程语言和.NET 2.0框架的实践指南。这本书旨在帮助开发者全面理解这两个关键技术的结合,以及如何在实际项目中有效地应用它们。下面将详细阐述C#语言和.NET...