关于接口是值类型还是引用类型的猜测

      虽然工作也有几年时间了,但时常能暴露出基础知识薄弱的问题,因此,有空闲时间时总喜欢把缺下的功课补齐。

      今天想讨论的是,接口到底是引用类型还是值类型。

      要想说清楚接口到底是引用类型还是值类型,就需要先解释一下值类型跟引用类型。

      值类型,简单理解就是继承自ValueType的类型,除过可空类型(如int?,bool?)外,值不能为null,通常情况下存储在栈中。而引用类型不从ValueType继承,其值可以为null,存储在托管堆中,由GC负责清理。

      既然大致了解了什么是值类型和引用类型,或者说,值类型和引用类型的区别,那么有一个简单粗暴的办法判定接口类型问题----可以调用Object的实例方法GetType(),得到Type实例。通过Type的实例属性BaseType,直接判断接口的基类型。只是,接口可以理解成一种抽象类型,抽象类型是不允许实例化的,只能由实现类实例化。值类型以及引用类型都可以实现接口,所以通过基类型的方法并非个好方法。

      偶然间发现一段代码:

  const int i = 5;

  IFormattable ftt = i;

  这里不得不提到另一对概念,装箱和拆箱。装箱会将值类型经过一系列包装放到一个箱子,转换为引用类型,然后存放到托管堆上面。拆箱和这个过程相反,从托管堆上面拿到这个箱子,经过一些列的拆分操作,从引用类型转换成值类型。当然,拆箱过程涉及类型检查,这里不细说。总之,装箱可以简单理解为值类型-->引用类型,拆箱可以简单理解为引用类型-->值类型。有了这些理解就够了。

  再来观察上面的代码段,i 为int类型,int类型是系统自定义的值类型。IFormattable是接口类型,如果接口类型是值类型,那么IFormattable ftt = i 是值类型到值类型的转换,将不存在装箱操作。如果接口类型是引用类型,那么IFormattable ftt = i 必然存在从值类型到引用类型的转换,即装箱操作。那就不多说了,采用IL查看工具看下源码就明白了!

  

using System;

namespace Interview
{
    class TestInterfaceType
    {
        public void Test()
        {
            const int i = 5;
            IFormattable ftt = i;
        }
    }
}
Source Code

  上面是源码,下面是IL源码(可以通过ILSpy、Reflactor或者VS 自带的ILDasm工具查看IL代码):

.method public hidebysig 
    instance void Test () cil managed 
{
    // Method begins at RVA 0x2330
    // Code size 9 (0x9)
    .maxstack 1
    .locals init (
        [0] class [mscorlib]System.IFormattable ftt
    )

    IL_0000: nop
    IL_0001: ldc.i4.5
    IL_0002: box [mscorlib]System.Int32
    IL_0007: stloc.0
    IL_0008: ret
} // end of method TestInterfaceType::Test
IL Code

  亮点在于 IL_0002: box [mscorlib]System.Int32 这一行,其中 box 就代表装箱指令。基本就可以断定,接口属于引用类型了。

  当然,这些只是个人的一些理解,如果有不妥的地方,欢迎各位指正。


           

posted @ 2016-03-12 11:56  LightSmaile  阅读(2535)  评论(0编辑  收藏  举报