【原创】《.NET本质论》读书笔记(二)

 

用类型编程

 

这一章的主要内容是介绍用类型可以做什么,以及一些函数的使用范例。我认为有用的部分主要有三个:类型的内存分布结构、类型函数的使用、元数据扩展(利用属性)

1、引用、对象、类型在内存中如何分布的?

     这部分解决了我这方面的很多疑惑,直接上图:

未命名

 

使用对象时,我们通过“引用”来使用对象,所谓“引用”不过也是托管指针罢了(托管指针是指向托管内存的一个指针),途中的Reference即是。引用指向的对象在内存中怎么存储的呢?首先有一个sync#,这个是用来记录资源信息的,锁之类,这个我还没有涉及,不多说。重要的是后面有一个htype的类型指针,这个指针指向一块不透明(ms没有文档公布)的数据结构,这个结构里记录了类型的的基本信息,所以同一Type的实例的htype段存着同一个值,那就是指向这个Type结构的指针。最后就是对象的field了。这个图解决了我关于对象如何找到类的问题。

 

那么,类结构里又怎么存的呢?下图:

未命名

类型的结构中有两个重要内容——接口表和base指针。接口表是说这个类型如果实现了任何接口,都会在这个表里有个entry, 每个entry都有一个指向接口结构的指针。base指针则是指向这个类型的直接父类的内存结构。

这样我们就知道CLR是如何判断一个类是否实现(继承)了某个接口(类),只需要在借口表里顺序查找(或是根据base指针逐级查找即可)。可以看出,类型的向上转换的开销来源于此。至于强制转换,则有另外的开销,这里不述。

PS:在c#中,类型转换用as、is。不同之处在于,as将实例直接转换为目标类型返回,is则是返回bool值。如果的确需要转换,那么用as比较好,用is的话,判断时强制转换一次,真正转换又一次,相当于重复执行了一遍。

 

2、虽然Type的内存结构没有明确的文档定义,但是,运行库提供了编程方法来取得这些信息,这就是System.Reflection。

利用Reflection(反射机制),我们可以取得想要的一切Type信息。下面选几个比较有用的。

 

获取Type的方法:

a. System.Object.GetType() 取得值或对象的类型,返回System.Type类型。

b.  typeof 运算符,传入类型字符串,返回System.Type,例如,typeof(Int32)。

c.   Assembly.GetType() 取得程序集中的类型,需要传入字符串。例如:assm.GetType(“System.Type”)

Type类包含的有用方法:

a. 类型兼容性测试方法:

   IsSubclassOf(Type t), 当前类是t的子类型,返回true。

   IsAssignableFrom(Type t),当前类与t类型相同 | 当前类是t父类 | 当前类是t实现的借口,则返回false。

   IsAssignableFrom()用的多,因为可判断情况多。

b. Type.GetInterfaces()获取Type实现的接口。

c. Type可以得到的信息很多,如下图所示:

未命名

类的层级结构图如下:

未命名

因为类成员有访问限制,所以在调用GetMembers()等方法时可以传入BindingFlags参数,用以指定需要的成员。

 

3、三个特殊方法

a. getter和setter

这个不陌生,有这两个方法的字段叫做“property“,其实,类型中的property编译时,会分别产生get和set函数。如下所示:

没主动写.cctor时

 

产生的中间代码如下:

image

编译器自动添加了get_Price和set_Price两个函数。Price的中间码如下:

.property instance int32 Price()
{
  .get instance int32 LinaTest.Vinegar::get_Price()
  .set instance void LinaTest.Vinegar::set_Price(int32)
} // end of property Vinegar::Price

当调用Price property时,调用的实际是这两个函数。

 

b. 事件 event

    对于类型中的event,编译器会自动生成add方法和remove方法。例如:

event使用

 

中间码为:

image

OnSubmit的中间码为:

.event [mscorlib]System.EventHandler OnSubmit
{
  .removeon instance void LinaTest.Vinegar::remove_OnSubmit(class [mscorlib]System.EventHandler)
  .addon instance void LinaTest.Vinegar::add_OnSubmit(class [mscorlib]System.EventHandler)
} // end of event Vinegar::OnSubmit

和property道理一样的。

 

c. indexer

和上面的property、event同理,indexer也是将对成员的访问重定向到编译器自动生成的方法。代码如下:

还是vinegar类,加了event成员

 

中间代码为:

image

.property instance int32 Item(int32)
{
  .get instance int32 LinaTest.Vinegar::get_Item(int32)
} // end of property Vinegar::Item

注意这里写了两个indexer,一个用string索引,一个用int。

 

4、元数据扩展

经常能看到在class、method、field前面有用中括号括起来的一些东西,这就是属性。

属性也是一个类型,是继承于System.Attribute类的,c#、c++里用中括号来使用属性。属性的作用就是将一些信息写入到元数据中,以便让编译器或者其他程序读取。由于Attribute也是有构造函数的(不然怎么让CLR初始化),使用格式就是如下:(这是自定一个属性,当然库里也有其他属性可用)

自定义属性

 

这时元数据如下:

image

在代码中如何取出属性值呢?

取出自定义属性信息

 

使用属性时,不只可以通过构造函数的形式,也可以用名字传递,不过需要属性类中的field是public的,而且,不能写带这个参数的构造函数了。根据这个修改上面的代码,如下:

利用名字传递特性

 

关于属性的使用一个例子:msdn上举的,拿过来用啦~

一个实际例子

 

这段代码执行时,编译器会读取Obsolete属性,所以在编译过程中会有Obsolete的警告。

posted @ 2010-03-15 19:43  linaelf  阅读(670)  评论(0编辑  收藏  举报