class program
    {
        public static void Main(string[] args)
        {            Person p1 = new 中国人();
            中国人 p2 = (中国人)p1;
            Console.WriteLine(p2.j);
            Console.ReadKey();
        }
    }
    class Person
    {
       public int i = 4;
    }
    class 中国人 : Person
    {
       public int j = 8;
    }
上述代码,p1指向了托管堆中“new中国人”的Person部分,这句话,应该没错吧!!!那(中国人)p1就是把p1强制转换成中国人,既然p1都没有中国人的特有内容,只具有Person的内容,怎么能把p1转换成中国人呢?不懂?

解决方案 »

  1.   

    看这里:
    class 中国人 : Person
    Person包含类,中国人相当一个派生类,嵌套了;
      

  2.   


    什么叫做“Person部分”?这个十有八九是错的。如果一定要附会一些c术语可以这样说:“new 中国人”实例化了一个对象,p1指向这个对象。这个对象有个指针指向“中国人”类型对象。而这个中国人类型对象,它内部又有一个所继承的类型列表,其中第一项指向“Person”类型对象。
      

  3.   

    P1应该是地址,能放什么,能否强制转化,编译器做了限制。实际放的内容要看NEW的什么。
      

  4.   

    引用类型间子类向父类转换,其实可以理解为换了块“招牌”,数据格式基本没变。Person p1 = new 中国人()表示创建了个中国人的实例,然后转换为Person,其内部的数据格式还是中国人的数据格式,但表示身份的牌子换了一块,主要给编译器看。子类转换为父类,子类的所有成员都保留下来,没有丢失,但根据父类的特性,只提供较少的对外交流接口(属性、方法等)供外部使用。所以当父类转换回子类时,不会丢失什么。比如某种型号电视机遥控器,原配的可能有50个按键,不仅提供基本功能,还提供一些高级功能(比如单独听)。有一天遥控器坏了,买了个万能遥控器回来,这个万能遥控器只有40个按键,仅提供基本功能。若有一天我买到了本型号的遥控器,那10个按键的功能还是能用的。
    所以引用类型子类和父类间互相转换,不需要大量运算,效率是很高的。这根装箱拆箱不同。
    比如TextBox等转换为Control类,这时换了块牌子,告诉编译器我是Control类,Text属性暂时被隐蔽了,转换回TextBox,又把牌子换了回来,这时Text属性又能用了。以上是个人猜测,仅供参考。
      

  5.   

    你在Person p1 = new 中国人();之后再执行一句Console.WriteLine(p1.GetType().Name);
    可以看到实际还是“中国人”。
      

  6.   

    如果再进一步用c的方式来解释,还可以说:类型对象与普通的类型实例化对象不同,它们不再同一个堆中。类型对象所在的堆在系统的最高处,它的变动很小,不会被GC整理。运行时,系统经常要要到类型对象中去找到相关方法函数、静态对象的引用,等等。实际上越是底层的东西,CLR的不同版本都可能重新修改其结构和机制。真正不变的那些公开的东西,而不是挖掘地底下的机制。CLS规范了那些公开的东西,比如说他不管.net上不同的语言如何实现对象继承,但是它规定了统一的行为标准,它指出不管变量声明为什么类型都应该指向你的“new 中国人”的那一个、也只有这一个对象,而不是说“中国人对象还有一个Person对象”这种概念。
      

  7.   

    而不是说“中国人对象还有一个Person对象”这种概念 -->  而不是说“中国人对象内部还有一个Person对象”这种概念
      

  8.   

    A b = new B();
    //因为B是A的一个子类,父类可以指向(引用)子类对象
      

  9.   

    (中国人)p1;是把p1强制转换成中国人了,是吧,这个说法没错吧,那之后,p1就完全指向中国人吗?或者说p1也就具有中国人的特有内容了吗?如果p1没有中国人的特有内容,那(中国人)p1;p1强制转换成中国人了,又是什么意思?这句话包含了哪些意思?
      

  10.   

    Person既然作为中国人的父/基类,那Person的实例肯定具有了中国人所有的特性,那这句 Person p1 = new 中国人();
    就不难理解,但是你不能说中国人就具有了人的所有特征,比如说中国人没有美国人黄色的头发和蓝色的眼睛(打比方美国人就这样吧,其中美国人也继承了人这个类) 理解了这个,那这句 中国人 p2 = (中国人)p1;
    也就不难理解,作为派生类的一个实例p2要想具备p1的所有信息,必需得用到强制转化把人类的对象p2转化成中国人的实例!这样说,你应该明白吧?
      

  11.   

    嗯,能理解,那问一下,P1既然已经转换成中国人了,那P1也应该有中国人的特有内容吧,那就能访问p1.j了吗?
      

  12.   

    什么叫做“强制转换”呢?var ren=(中国人)p1;这个代码首先看看p1所引用的对象是不是中国人,是那么就什么也不做直接把ren变量引用这同一个对象就行了;如果不是,那就找找看有没有“从Person到中国人”的用户自定义转换方法,没有则编译异常,有则编译为调用这个用户自定义转换函数的语句。
      

  13.   

    sorry,上面两个“编译”写错了,应该是“运行”。这是运行时执行的,编译时检查不出来。所以叫做“强制”转换。因为编译器检查不出来可能在运行时才会出现的异常。
      

  14.   


    这是不对的。运行时p1不但可能实例化为中国人,也可能实例化为国产女优。Person不可能在编译时就具有了将来一切的、“万能的”具体人类(子类)的所有特性。所以这个是不对的。多态不是这个意思。
      

  15.   

    实例就是实例,类型就是类型...没有什么“中国人具有人的特性”或者“人具有中国人的特性”这类奇怪的描述...一个人只所以被人认为是中国人,仅仅是因为他当前的属性...假如他明天换了别的国籍,这个人仍然是这个人,他的生物特性不会发生任何改变...其实这个例子本身就有问题,国籍作为人的属性是可变化的而不是可继承的...你把那个“中国人”的类型改成“黄种人”即可继承的“人种”就更容易理解...实例在实例化的那一刻他的类型就被确定了...一个黄种人从出生那一刻就确定了他到死都不可能变成白种人或黑种人,但首先他是个“人类”,“人类”是他的基类型...所谓“强制转换”仅仅是改变实例当前的分类归属,并不会对实例本身有任何影响...但分类和继承都是有规则的,所以“黄种人”同时也是“人”,但“人”未必是“黄种人”...所以不存在什么“Person既然作为中国人的父/基类,那Person的实例肯定具有了中国人所有的特性”的概念...如果非要拿什么“托管堆指向”这种莫名其妙的概念往上套,你可以认为“引用”仅仅是“实例”的“身份证”...“身份证”可以换,“实例”不会变...去找本OOP入门书好好学学面向对象的基础知识,把基础概念先搞明白...
      

  16.   

    强制类型转换并不像许多人以为的那样是对象拷贝的。实际上大多数时候它就是让变量p2可以安全地引用p所引用的同一个对象。可能是“强制”这个词让人感到敬畏,所以许多人不相信它只不过是复制对象的引用这么简单的动作。其实如果你打印p1和p2的GetHashCode(),会发现它们总是相同的。那么我也举一个真的不是复制对象引用的例子好了,这也是强制类型转换:class 马
    {
        public int 齿龄;
    }class 香蕉
    {
        public int 成熟度;    public static implicit operator 马(香蕉 x)
        {
            return new 马 { 齿龄 = x.成熟度 / 5 };
        }
    }在这个例子里,我无厘头地定一个把香蕉对象可以强制类型转换为马对象的例子。使用它的代码就是var a = new 香蕉 { 成熟度 = 90 };
    var b = (马)a;运行它你就会发现变量b引用了一个齿龄为18的马对象。但是如果你打印a和b的GetHashCode(),它们总是不相同的。可见,虽然我们声明不同变量时类型不同,于是编译器会帮我们检查代码中的各种类型不匹配的代码,但是它执行的结果,有时候(我猜大多数几乎所有用法都仅仅是这种)是让不同变量指向相同的对象,可是有时候它是让系统调用自定义的类型转换方法(而这很可能就是为了产生与香蕉完全无关的新的马)。
      

  17.   

    我猜大多数几乎所有用法都仅仅是这种  -->  我猜大多数人到现在几乎所有用法都仅仅是这种我们大多数人都是lz这种使用强制类型转换的用法。当你new一个中国人对象,然后让变量p1引用它,然后再把它前置类型转换为中国人并用变量p2指向转换结果,此时只有一个实例化为中国人的对象存在,没有第二个独立的对象存在。编译器无法检测出这个强制类型转换是否没有异常,只有在运行时才去检查,所以叫做强制类型转换。
      

  18.   

    自定义类型转换有什么用呢?我随便举个例子,比如你有一个“演员”列表,然后有一个“表演班”对象,你现在想把表演班里的所有学员都加入演员列表中,代码List<演员> list;
    表演班 team;
    ....
    list.AddRange(team);   //把team中的所有成员加入演员表这个代码很可能编译不过去。可是你不需要修改这个代码,只要为表演班类型增加一个自定义的转换为List<演员>的方法,上述代码就编译过去了。可见你的代码表现力就丰富了,代码成文自明。再比如代码var clause = (主语)s1 + (谓语)s2 + (宾语)s3;这个代码表示三个字符串被转换为三类不同的语言元素,然后被两个自定义重载的+运算符操作,从而产生了“句子”这个新对象。这样的代码是不是成文自明?这就需要自定义类型转换和自定义运算符重载技术,才能写出能被用户一目了然的代码(而不是那种只有程序员才习惯看的代码)。