【原创】《.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时
class Vinegar
    {
        public int Price
        {
            get { return this.price; }
            set {this.price = value;}
        }
        private int price = 1;
    }

 

产生的中间代码如下:

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使用
class Vinegar
    {
        public int Price
        {
            get { return this.price; }
            set {this.price = value;}
        }
        private int price = 1;

        public event EventHandler OnSubmit;
    }

 

中间码为:

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成员
class Vinegar
    {
        private int price = 10;

        public int this[string name]
        {
            get
            {
                if (name == "price")
                    return this.price;
                else
                    return 0;
            }
        }

        public int this[int index]
        {
            get
            {
                if (1 == index)
                    return this.price;
                else
                    return 0;
            }
        }
    }

 

中间代码为:

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初始化),使用格式就是如下:(这是自定一个属性,当然库里也有其他属性可用)

自定义属性
class MyAtrribute:System.Attribute
    {
        private int count;
        public MyAtrribute(int count){this.count = count;}
        public int Count
        {
            get { return this.count; }
        }
    }

    [MyAtrribute(5)]
    class Vinegar
    {
        private int price = 10;
    }

 

这时元数据如下:

image

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

取出自定义属性信息
static void Main(string[] args)
        {
            Vinegar v = new Vinegar();
            Type t = v.GetType();
            object[] os = t.GetCustomAttributes(false);
            Console.WriteLine((os[0] as MyAtrribute).Count);
        }

 

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

利用名字传递特性
class MyAtrribute:System.Attribute
    {
        public int count;
    }

    [MyAtrribute(count=5)]
    class Vinegar
    {
        private int price = 10;
    }

 

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

一个实际例子
using System;
public class Example
{
    // Specify attributes between square brackets in C#.
    // This attribute is applied only to the Add method.
    [Obsolete("Will be removed in next version.")]
    public static int Add(int a, int b)
    {
        return (a + b);
    }
}
class Test
{
    static void Main()
    {
        // This generates a compile-time warning.
        int i = Example.Add(2, 2);
    }
}

 

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

转载于:https://www.cnblogs.com/lina/archive/2010/03/15/1686611.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值