也说new

今天看到了Anytao的[你必须知道的.NET] 第五回:深入浅出关键字---把new说透。Anytao这一系列文章写得都非常好,其实甚至正是我一直想写的。比起各种应用层面上的技巧,我更喜欢研究.NET的底层机制。但是光顾了自己研究了,也没好好写东西给大家分享。

《把new说透》这篇文章介绍的内容不错,但Anytao文字上可能没有表达得很清晰。C# 2.0中关键字new有三种作用——1)作为修饰符覆盖父类中的virtual成员,2)作为运算符创建对象,3)作为泛型类型中对类型形参的约束。

new的这三种功能其实是完全不相干的,Anders Liu个人感觉作为文章来说,应该完全分开在不同的小节中去介绍。

1 new修饰符

new修饰符用于修饰类型成员(属性、方法等)。

(懒得画图写代码了,所以采用纯文字描述。大家可以看Anytao的代码)

当父类中编写了virtual方法时,子类出现了相同签名的方法时,必须冠以override或new运算符。

如果使用override运算符,则可以实现“多态”。即:将子类对象转成父类型后,调用virtual方法,实际上执行的是子类中的方法代码。

而如果使用new运算符,则不会出现上述情况,将子类对象转成父类型后,调用virtual方法,实际上执行的是还是父类中的方法代码。

2 new运算符

new运算符用于创建对象。当使用new运算符创建对象时,会发生下列事情:

- 根据元数据中的类型信息,计算对象所需空间,根据值类型/引用类型的区别,在栈或者堆中开辟适当大小的存储区域。
- 根据元数据中的类型信息,对类成员空间进行排列。并初始化成员。(*)
- 调用构造器。
- 返回对象引用。

所以,string s = new string("asdf");实际上是首先根据string类型信息在堆上开辟存储空间,排列其成员,然后调用string(string s)签名的构造器,最后返回新对象引用,并通过等号赋给变量s。

因此,Anytao提到的int i与int i = new int()的区别也就出来了。

但这里Anytao没有陈述清楚的是,int i出现的位置——int i即可以出现在类中,成为一个字段(域);也可以出现在方法中,成为一个变量(还有一种是出现在方法参数中,但就其语义,和变量是类似的)。

如果int i是一个字段(域)定义,那么两者是没有任何区别的。因为,注意上面第二条带(*)的部分,当客户代码初始化当前类的对象时,会同时初始化这个i,将其值置为0。

如果int i是一个变量定义,那么,int i只是声明了一个局部变量,此时的i不能直接使用,必须首先赋值(如果未赋值就使用,会得到一个编译错误)。而int i = new int()则对i进行了一个初始化。

3 new约束

在泛型类型定义时,可以使用where指定一些约束,其中一种就是new约束。new 约束要求用作类型实参的类型必须带有公共无参构造器。如class A<T> where T : new();这里只有带有公共无参构造器的类型才能用作T。

需要注意两点,1)如果同时存在其他约束,那么new约束应该是最后一个。2)不能用new(int i)的形式来约束拥有指定签名的构造器。

好了,希望Anders Liu能给Anytao梳理一下文字。

posted @ 2007-04-29 13:59 Anders Liu 阅读(2825) 评论(27)  编辑 收藏 网摘

  回复  引用  查看    
#1楼2007-04-29 14:30 | teana      
"好了,希望Anders Liu能给Anytao梳理一下文字".....................
  回复  引用    
#2楼2007-04-29 15:07 | 航天奇侠
赞同,鼓掌。
  回复  引用  查看    
#3楼2007-04-29 15:28 | 非我      
都写得不错
  回复  引用  查看    
#4楼[楼主]2007-04-29 15:36 | Anders Liu      
@teana

就是说希望Anders的这篇文章能辅助大家搞清楚Anytao文章的思路

  回复  引用    
#5楼2007-04-29 16:51 | ikaiser[未注册用户]
写的非常好,简单易懂,收藏了
  回复  引用  查看    
#6楼2007-04-29 16:54 | Anytao      
大开眼界,博客园真正需要的是交流,你的意见让我也更加理清了思路,有些地方的描述是该学习学习,谢谢。
  回复  引用  查看    
#7楼2007-04-29 16:58 | teana      
偶理解错误。。。不好意思@Anders Liu

  回复  引用  查看    
#8楼[楼主]2007-04-29 17:05 | Anders Liu      
@Anytao
还是得多些你的这个系列~

今后我争取也多写点出来!

  回复  引用  查看    
#9楼[楼主]2007-04-29 17:06 | Anders Liu      
@teana
最后这句的确是我说得不好,嘿嘿……表达力有限~

  回复  引用  查看    
#10楼2007-04-29 17:26 | Anytao      
@Anders Liu
感谢共享,希望我系列的后续帖子都能得到你的指正和指导

  回复  引用    
#11楼2007-04-29 20:13 | sopper[未注册用户]
顶一下

总算是对
List<string> list = new List<string>();
这样的定义有些理解了

关注中……

  回复  引用  查看    
#12楼2007-04-29 21:06 | 装配脑袋      
“- 根据元数据中的类型信息,计算对象所需空间,根据值类型/引用类型的区别,在栈或者堆中开辟适当大小的存储区域。”

这句话是不准确的,new作用于引用类型时的确是这样,但作用于值类型时是不会去栈上开辟空间的,因为栈不是动态分配的。实际上作为局部变量的值类型,其空间是在栈桢创建的时候就分配好的。此时new就仅相当于调用了一个返回这个值类型的静态方法。

  回复  引用    
#13楼2007-04-29 22:29 | Anders Liu[未注册用户]
@装配脑袋
精辟! 我的确忽略了这一点……

  回复  引用  查看    
#14楼2007-04-30 00:14 | 细水长流      
5465
  回复  引用  查看    
#15楼2007-04-30 20:27 | Clark Zheng      
高手!
  回复  引用  查看    
#16楼2007-05-01 09:12 | Boler Guo      
标记一下,回头看看

  回复  引用    
#17楼2007-08-13 11:10 | leeses@126.com[未注册用户]
请教两个问题:
1.如果父类中的方法不是virtual的,子类中出现相同签名的方法也要用new吧!
2.
"而如果使用new运算符,则不会出现上述情况,将子类对象转成父类型后,调用virtual方法,实际上执行的是还是父类中的方法代码。"
能否提供代码说明,不盛感激!

  回复  引用  查看    
#18楼2007-10-28 12:51 | flankerfc      
new 1)作为修饰符覆盖父类中的virtual成员
----------------------------------------

这个 覆盖是用override的吧(或者叫重写)
new作为修饰符应该叫做隐藏

(这些中文翻印都很混乱的 不知我理解的对不)

  回复  引用  查看    
#19楼2007-11-25 15:01 | Klesh Wong      
即使父类成员不是virtual,一样可以使用new关键字盖之……
  回复  引用  查看    
#20楼2007-12-17 11:31 | 林骄      
楼主你总结得挺好的,解决了我很多的疑问。
请问一个问题:元数据中的类型信息,计算对象所需空间。
这是怎么来实现的?都是有哪些需要分配空间。字段需要,函数参数需要,那么函数代码呢?
元数据中的类型信息包括了继承自父类的字段和方法,这些会分配空间并出现在子类的方法表中么?




发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 732209




相关文章:

相关链接: