• 【原创】《.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的警告。

  • 相关阅读:
    QuantLib 金融计算——案例之固息债的关键利率久期(KRD)
    Elasticsearch 最佳运维实践
    Ansible常用的Ansible Module
    中国七夕节( Chinese Valentine's Day)IT的浪漫情结
    Leetcode之C++刷题生死战
    无需Root可自动定时发送微信和短信的黑科技APP,支持跳过开屏启动广告
    tmp
    【整理中】可靠性基础_抽样检测
    【汇总】命名及标识
    Treelist父节点上下移
  • 原文地址:https://www.cnblogs.com/lina/p/1686611.html
Copyright © 2020-2023  润新知