当前位置:编程学习 > C#/ASP.NET >>

初读CLR Via C# 之类型基础(一)

 

最近由于出差在外地,再加上自己在学习一些新的知识,所以迟迟没有再看这本书,更没有更新此系列的后续内容,但是这本书确实是本好书,不想就此放下,碰巧今天也比较悠闲,于是今天又一次回顾了之前的内容,并开始了新的阅读。今天开始尽量尽快进行更新。

     在开始之前,我想先讲一下继承的概念。大家都知道,面向对象是由:封装、继承、多态来体现的,至于什么是封装,不是很明白的朋友请去Google找下答案吧。那么,什么事继承?“继承”我们可以理解为是一种在无需重新编写类本身的情况下,对该类进行扩展的能力,并且可以使用现有类的所有功能。那么,通过继承创建的新类,我们可以称之为:“子类”或者“派生类”,反之,被继承的类我们称之为:“父类”或者“基类”,子类可以继承多个父类(派生类可以继承多个基类)但是一般情况下,一个子类只能有一个父类,要实现多重继承,可以通过多级继承来实现。继承的概念,笔者就先简单的介绍到这里吧,如果还是有些不明白的读者,可以在网上找些资料,相信博客园里面也有对继承概念有专门讲解的博文。

    在我们C#或者JAVA中,不管我们自己创建、调用任何一个类,它的顶级父类只有System.Object这一个类。也就是说,所有的类都是通过它进行衍生的。在我们创建完一个类的时候,类里面任何方法、字段、属性都不去写,在另一个类中进行实例化之后,都会看到一些方法如:Equals();GetHashCode();ToString();GetType()等。那么这些方法就是System.Object类中的方法。这就是一个继承的概念,在我们创建一个类的时候,它继承了System.Object类、拥有了System.Object类中的方法,并且对System.Object类进行了一个扩展。上面我提到一个实例化。我们都是用new关键字来对一个类进行实例化(非静态类(static))在我们new了一个实例化的时候,会自动在堆上为我们创建一个“类型对象指针”和“同步索引快”,而且CLR会自动给我们分配一个该类所需要的内存,这个内存的大小根据类中的字段所需要的字节数进行设置,然而,最终new关键字会调用System.Object的构造函数,并进行返回,会返回给新对象一个引用(对象指针),我们所有调用类中的方法,属性的时候,是根据这个引用的内存地址进行操作的。也可以理解为我们操作内存(实际上是CLR帮我们完成的),到这里可能会有一个问题,就是,这个对象的内存我们 如何去释放。这就轮到我们CLR的垃圾回收机制(GC)进行检测,并对不再访问的内存进行自动释放操作。垃圾回收机制将在基本内容更新过以后进行更新。

   在我们日常的使用中,会非常频繁用到类型的转换。那么类型是可以随便进行转换的么(比如Person类型转换成DateTime类型)?答案是不可以。可是为什么不可以?首先,CLR最重要的特性之一就是安全性。这个安全性就已经把我们限制死了。在我们使用反射的时候会用到GetType()方法,这个方法是一个非虚方法,是不可重写的方法,这个方法不可重写,就意味着我们不能把Person类通过GetType方法伪装成DateTime类型,并进行返回。在类型转换的时候,CLR准许我们将一个父类强制转换成它的子类,而不需要任何特殊的语法把子类转换成父类,例如:

namespace Chapter4

{

    class Program

    {

        static void Main(string[] args)

        {

            //子类转换成父类不需要强制转换

            Object o = new Person();

            //父类转换成子类必须进行强制类型转换

            Person p = (Person)o;

           

        }

    }

 

    //该类继承自System.Object类

    internal class Person

    {

        private string name;

        private int age;

    }

}

 

那么两个类都集成自Object类,为什么就不能相互进行转换?CLR在每次进行转换的时候,都会对转换的对象进行检查,确定o是否引用自Peron对象,或者是否是从Peron类派生 的任何子类,如果是CLR将准许转换,否则将抛出异常,就拿刚才说的Peron转换成DateTime为例,代码如下:

 class Program

    {

        static void Main(string[] args)

        {

            //子类转换成父类不需要强制转换

            Object o = new Person();

            //父类转换成子类必须进行强制类型转换

            Person p = (Person)o;

            //将Person的子类转换成Person

            Man m = new Man();

            MagicPerson(m);

            //构建一个DateTime对象,并转换成Person

            DateTime dtNow = DateTime.Now;

            MagicPerson(dtNow);

           

        }

 

        private static void MagicPerson(object o)

        {

            Person p = (Person)o;

        }

    }

 

    //该类继承自System.Object类

    internal class Person

    {

        private string name;

        private int age;

    }

    //该类继承自Person(最高基类为System.Object)

    internal class Man : Person

    {

        private string sex;

    }

 

以上的代码完全可以通过编译,并运行。那么第二次运行到MagicPerson方法的时候,CLR会给我们抛除一个为:System.InvalidCastExiception异常,并告诉我们转换失败。为什么会转换失败?我们来分析一下CLR进行类型转换时候的工作步骤。首先在编译时,编译器并不知道MagicPerson中参数o到底是谁的引用,所以编译器准许代码编译通过。但在运行时,CLR知道了第一次进来的o是Man的引用,并继承自Person类,第一次转换成功。当再次进入MagicPerson方法的时候对象的引用为DateTime的引用。CLR会核实DateTime类型是否继承自Person或者是否是Peron类型,发现并不继承自Person类也是Person类型,便抛出异常信息终止转型。

     在以上的代码例子中,MagicPerson方法在实际运用中参数应该设定为Person类型,这样的话,编译器就会报错,在造成更大损失之前,把罪恶的种子掐死在摇篮之中。

     先去吃饭,吃饭回来之后接着更新。

 

 

作者 LouisLee

补充:软件开发 , C# ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,