牛人那么多,我就冒个泡,多句嘴 Object类里面的getype方法有如下特性 [MethodImplAttribute(MethodImplOptions.InternalCall)] public extern Type GetType(); MethodImplOptions.InternalCall,据说这玩意是CLR实现的方法,偶个人觉得,会"创建"一个父类对象就像是你要创建一个男人,首先应该要先建立一个没有性别特征的人,然后再把缺的东西加上(这说法很黄很暴力)可以这么说吧,当你创建出一个男人之后,你就只有一个男人,而不是一个人外加一个男人(很邪恶的说法)但是,男人也是人.人能做的事情,男人也能做,男人能做的事情,人不一定能做(邪恶~~~)你不应该把父类和子类分割开,认为他们是独立的,实际上他们是一个整体,包含的关系如果你觉得我说的太邪恶,请直听SP1234说的话
class a { int dataA; void virtual funA(){} }class b { int dataB; override void funA(){} void funB(){} }类a的MethodTable大概是:funA、参数、方法地址。。 类b的MethodTable大概是:base.funA、参数、方法地址。。 this.funA、参数、方法地址。。 funB、参数、方法地址。。类a的对象实例大概是:类a的MethodTable的地址、dataA 类b的对象实例大概是:类b的MethodTable的地址、dataA、dataB 只是示意、clr的实现会更全面
{
class Program
{
static void Main(string[] args)
{
new B().我到底是谁呢();
Console.ReadKey();
}
} class A
{
public virtual void 我到底是谁呢()
{
Console.WriteLine("我的类型是{0},我的HashCode是{1}。", this.GetType().FullName, this.GetHashCode());
}
} class B: A
{
public override void 我到底是谁呢()
{
base.我到底是谁呢();
Console.WriteLine("我的类型是{0},我的HashCode是{1}。", this.GetType().FullName, this.GetHashCode());
}
}
}
我的类型是ConsoleApplication1.B,我的HashCode是45653674。
base关键字也不是对父类对象的引用,它只是用于从派生类中访问基类的成员。
A:挂上调试器和SOS可以看到具体的分配情况。对象在托管堆上分配的时候记录一个方法表指针,这个指针描述这个对象的类型。
每个对象都有他自己独立的存储空间,从内存上来看两者没有什么不同。Q:最开始在堆内存里分配了父类对象的,那么在子类对象中,是不是还包含了一份父类对象的拷贝,或者只有一个父类的base引用呢?
A:实现上没有这个指针,前面说了,这两个对象在内存中是相互独立的。
实现OO的问题不在于内存中如何放,而是CLR如何识别对象,这里元数据是当之无愧的MVP!至于OO的概念,sp1234大仙都说了,俺不废话了
MethodTable的地址指针是类方法在内存中的描述(这里包含类的方法和方法的实际地址等),同一个类的所有实例的方法都是同一个地址,在方法执行时,对象实例本身是作为参数传入方法内的。对于类继承,子类对象的MethodTable、父类对象的MethodTable关系也不是简单的包含,abstract方法应该是覆盖,其他方法包含base.方法和this.方法等等
Object类里面的getype方法有如下特性
[MethodImplAttribute(MethodImplOptions.InternalCall)]
public extern Type GetType();
MethodImplOptions.InternalCall,据说这玩意是CLR实现的方法,偶个人觉得,会"创建"一个父类对象就像是你要创建一个男人,首先应该要先建立一个没有性别特征的人,然后再把缺的东西加上(这说法很黄很暴力)可以这么说吧,当你创建出一个男人之后,你就只有一个男人,而不是一个人外加一个男人(很邪恶的说法)但是,男人也是人.人能做的事情,男人也能做,男人能做的事情,人不一定能做(邪恶~~~)你不应该把父类和子类分割开,认为他们是独立的,实际上他们是一个整体,包含的关系如果你觉得我说的太邪恶,请直听SP1234说的话
{
int dataA;
void virtual funA(){}
}class b
{
int dataB;
override void funA(){}
void funB(){}
}类a的MethodTable大概是:funA、参数、方法地址。。
类b的MethodTable大概是:base.funA、参数、方法地址。。
this.funA、参数、方法地址。。
funB、参数、方法地址。。类a的对象实例大概是:类a的MethodTable的地址、dataA
类b的对象实例大概是:类b的MethodTable的地址、dataA、dataB
只是示意、clr的实现会更全面
★这种初始化顺序是由内存分配机制决定的,如我们调用MyClass myClass = new MyClass(), 其产生对象如下:
1. 计算该类中定义的所有实例字段的size和两个附加对象(type handler(类对象的指针)和syncBlockIndex(指向一块用来管理对象同步的内存))的size并一直递归到object对象,得到其需要分配内存大小, 看剩余内存是否够分配此对象, 不够会导致垃圾回收.
2. 首先构造MyClass的type对象,type对象包括静态字段和方法表,将其分配在托管堆的loader堆上,注意此对象将不会被GC自动回收, 其生命周期是从创建到AppDomain卸载.
3. 构造MyClass的实例字段,构造附加type handler并指向type对象, 构造SyncBlockIndex并指向同步内存.
4. 调用MyClass构造函数,此时会引起其父类初始化, 父类初始化循环上述过程,直至object对象完成创建, 再返回执行子类构造函数直至MyClass, 创建MyClass完成后, 返回其内存地址, 赋值给MyClass的this,并将其引用传给栈上声明的myClass.
new B().我到底是谁呢();
new B();先产生一个子类对象(怎么会有父类的对象呢?要是有的话,如果B为10层继承,那需要产生多少对象啊?MS有那么SB吗?)
Base只是一个关键字,CSC.EXE在编译C#代码时,把base.我到底是谁呢()直接编译为(当然不会是中文的了,应该是2进制代码)
IL_0002: call instance void CLR_Via_CSharp.A::我到底是谁呢()
IL_0007: nop
看见了吗?
base 关键字用于从派生类中访问基类的成员:
1> 调用基类上已被其他方法重写的方法。
2> 指定创建派生类实例时应调用的基类构造函数。
基类访问只能在构造函数、实例方法或实例属性访问器中进行。
从静态方法中使用 base 关键字是错误的。
MSDN是个好东东!
其实是,B在初始化的时候,A也被初始化了.从先后的顺序上看,是A先初始化因为 C# 是不允许 有多个CLASS 被同时继承的(例如菱形继承),这样也就避免了C++的 多基类问题(当然C++可以用虚基类实现的)楼上居然有人 把这个问题扯到 多太性上面去,