[你必须知道的.NET] 第五回:深入浅出关键字---把new说透

 发布日期:2007.4.28 作者:Anytao

©2007 Anytao.com ,原创作品,转贴请注明作者和出处。

 本文将介绍以下内容:

  • 面向对象基本概念
  • new关键字深入浅出
  • 对象创建的内存管理 

1. 引言

园子里好像没有或者很少把new关键字拿出来说的,那我就占个先机吧,呵呵。那么,我们到底有必要将一个关键字拿出来长篇大论吗?看来是个问题。回答的关键是:你真的理解了new吗?如果是,那请不要浪费时间,如果不是,那请继续本文的循序之旅。

下面几个 问题可以大概的考察你对new的掌握,开篇之前,希望大家做个检验,如果通过了,直接关掉本页即可。如果没有通过,希望本文的阐述能帮你找出答案。

  1. new一个class对象和new一个struct或者enum有什么不同?
  2. new在.NET中有几个用途,除了创建对象实例,还能做什么?
  3. new运算符,可以重载吗?
  4. 范型中,new有什么作用?
  5. new一个继承下来的方法和override一个继承方法有何区别?
  6. int i和int i = new int()有什么不同?

2. 基本概念

一般说来,new关键字在.NET中用于以下几个场合,这是MSDN的典型解释:

  • 作为运算符, 用于创建对象和调用构造函数。

本文的重点内容,本文在下一节来重点考虑。

  • 作为修饰符,用于向基类成员隐藏继承成员。

作为修饰符,基本的规则可以总结为:实现派生类中隐藏方法,则基类方法必须定义为virtual;new作为修饰符,实现隐藏基类成员时,不可和override共存,原因是这两者语义相斥:new用于实现创建一个新成员,同时隐藏基类的同名成员;而override用于实现对基类成员的扩展。

另外,如果在子类中隐藏了基类的数据成员,那么对基类原数据成员的访问,可以通过base修饰符来完成。

例如: 

new作为修饰符
  • 作为约束,用于在泛型声明中约束可能用作类型参数的参数的类型。

MSDN中的定义是:new 约束指定泛型类声明中的任何类型参数都必须有公共的无参数构造函数。当泛型类创建类型的新实例时,将此约束应用于类型参数。

注意:new作为约束和其他约束共存时,必须在最后指定。

其定义方式为:

    class Genericer<T> where T : new()
    {
        
public T GetItem()
        {
            
return new T();
        }
    }

实现方式为:

 

    class MyCls
    {
        
private string _name;

        
public string Name
        {
            
get { return _name; }
            
set { _name = value; }
        }

        
public MyCls()
        {
            _name 
= "Emma";
        }
    }


    class MyGenericTester
    {
        
public static void Main(string[] args)
        {
            Genericer
<MyCls> MyGen = new Genericer<MyCls>();
            Console.WriteLine(MyGen.GetItem().Name);
        }
    }
  • 使用new实现多态。 这不是我熟悉的话题,详细的内容可以参见 《多态与 new [C#]》,这里有较详细的论述。

3. 深入浅出

作为修饰符和约束的情况,不是很难理解的话题,正如我们看到本文开篇提出的问题,也大多集中在new作为运算符的情况,因此我们研究的重点就是揭开new作为运算符的前世今生。

Jeffrey Richter在其著作中,极力推荐读者使用ILDASM工具查看IL语言细节,从而提高对.NET的深入探究,在我认为这真是一条不错的建议,也给了自己很多提高的空间挖掘。因此,以下是本人的一点建议,我将在后续的系列中,关于学习方法论的讨论中深入探讨,这里只是顺便小议,希望有益于大家。
1 不断的学习代码;
2 经常看看IL语言的运行细节,对于提供.NET的认识非常有效。

文归正题,new运算符用于返回一个引用,指向系统分配的托管堆的内存地址。因此,在此我们以Reflector工具,来了解以下new操作符执行的背后,隐藏着什么玄机。

首先我们实现一段最简单的代码,然后分析其元数据的实现细节,来探求new在创建对象时到做了什么? 

new作为运算符

使用Reflector工具反编译产生的IL代码如下为: 

IL元数据分析

从而可以得出以下结论:

  • new一个class时,new完成了以下两个方面的内容:一是调用newobj命令来为实例在托管堆中分配内存;二是调用构造函数来实现对象初始化。
  • new一个struct时,new运算符用于调用其带构造函数,完成实例的初始化。
  • new一个int时,new运算符用于初始化其值为0。
  • 另外必须清楚,值类型和引用类型在分配内存时是不同的,值类型分配于线程的堆栈(stack)上,并变量本身就保存其实值,因此也不受GC的控制,;而引用类型变量,包含了指向托管堆的引用,内存分配于托管堆(managed heap)上,内存收集由GC完成。 

另外还有以下规则要多加注意:

  • new运算符不可重载。
  • new分配内存失败,将引发OutOfMemoryException异常。 

对于基本类型来说,使用new操作符来进行初始化的好处是,某些构造函数可以完成更优越的初始化操作,而避免了不高明的选择,例如:

string str = new string('*'100);

string str = new string(new char[] {'a''b''c'});

而不是

string str = "***************************************"

4. 结论

    我能说的就这么多了,至于透了没透,作者的能量也就这么多了。希望园子的大牛们常来扔块砖头,对我也是一种莫大的促进。但是作为基本的原理和应用,我想对大部分的需求是满足了。希望这种力求深入浅出的介绍,能给你分享new关键字和其本质的来龙去脉能有所帮助。 

言归正传,开篇的几个题目,不知读者是否有了各自的答案,我们不妨畅所欲言,做更深入的讨论,以便揭开其真实的面纱。 

参考文献

(USA)Stanley B.Lippman, C# Primer

(USA)David Chappell Understanding .NET

Welcome to Anytao.com 

广而告之

[预告]

另外鉴于前几个主题的讨论中,不管是类型、关键字等都涉及到引用类型和值类型的话题,我将于近期发表相关内容的探讨,同时还有其他的关键字值得研究,这是本系列近期动向,给自己做个广告。祝各位愉快。 

[声明] 

本文的关键字new指的是C#中的关键字概念,并非一般意义上的.NET CRL范畴,之所以将这个主题加入本系列,是基于在.NET体系下开发的我们,何言能逃得过基本语言的只是要点。所以大可不必追究什么是.NET,什么是C#的话题,希望大家理清概念,有的放肆。

温故知新

[开篇有益]

[第一回:恩怨情仇:is和as]

[第二回:对抽象编程:接口和抽象类]

[第三回:历史纠葛:特性和属性]

[第四回:后来居上:class和struct]

©2007 Anytao.com

原创作品,转贴请注明作者和出处,留此信息。

本贴子以现状提供且没有任何担保,同时也没有授予任何权利。
This posting is provided "AS IS" with no warranties, and confers no rights.

 

posted @ 2007-04-28 23:38 Anytao 阅读(9087) 评论(92)  编辑 收藏 所属分类: 01 [你必须知道的.NET]

  回复  引用  查看    
#1楼 2007-04-29 00:03 | teana      
沙发.顶..好文..没通过检测..认真的看了一遍...
  回复  引用  查看    
#2楼 [楼主]2007-04-29 00:07 | Anytao      
呵呵,希望能满足所需,做得更好
  回复  引用  查看    
#3楼 2007-04-29 07:18 | 菌哥      
认真读了,不错!
  回复  引用  查看    
#4楼 2007-04-29 08:41 | Terry Sun      
分析的很透彻, 值得细细品味, 只是有点看不懂IL
  回复  引用  查看    
#5楼 2007-04-29 09:28 | Sunshine      
楼主加油,希望最终出版成书,一定支持!
  回复  引用  查看    
#6楼 [楼主]2007-04-29 10:11 | Anytao      
@菌哥
感谢支持
  回复  引用  查看    
#7楼 [楼主]2007-04-29 10:12 | Anytao      
@Terry Sun
我也是略知一二,不过就像我文中提到的那样,还是应该做些这方面的熟悉,更好的了解IL,才能更好的了解.NET
  回复  引用  查看    
#8楼 [楼主]2007-04-29 10:12 | Anytao      
@Sunshine
呵呵,这倒没想过,不过希望能够做好这个系列,谢谢支持
  回复  引用  查看    
#9楼 2007-04-29 12:54 | Phinecos(洞庭散人)      
不错不错,支持一个
  回复  引用  查看    
#10楼 2007-04-29 13:04 | Anders Liu      
写得好! 你这一个系列都很好~~ 坚持啊~
  回复  引用  查看    
#11楼 2007-04-29 13:13 | teana      
楼主的文笔..貌似出书都应该没问题哇...加油..
  回复  引用    
#12楼 2007-04-29 14:13 | yiyioo [未注册用户]
刚看出点味道来.
就没了。..
555555
  回复  引用  查看    
#13楼 [楼主]2007-04-29 17:03 | Anytao      
本系列就想带起大家对.NET基本知识的思索空间,anytao在完成每个主题的时候都会再次思索学过的知识和经验,希望大家多提意见和建议。尤其是把各自在这方面的经验共享出来,给正在路上的朋友和自己更多的实惠。谢谢关注。
  回复  引用  查看    
#14楼 [楼主]2007-04-29 17:03 | Anytao      
@yiyioo
关注本系列就有了,:-),很多知识点是相互关联的,所以冰山一角不足以论道。
  回复  引用  查看    
#15楼 [楼主]2007-04-29 17:05 | Anytao      
@Phinecos(洞庭散人)
@teana
@Anders Liu
感谢大家的关注和支持,我也研读了Anders Liu 的补充,的确深入浅出,非常感谢。
  回复  引用    
#16楼 2007-04-29 18:21 | Siwei [未注册用户]
这个系列文章真是太棒了!谢谢anytao!很多平时不注意的知识点都巩固了,希望anytao能继续写下去,谢谢!
  回复  引用  查看    
#17楼 2007-04-29 18:24 | Daniel Pang      
不错的文章,关注...
  回复  引用  查看    
#18楼 [楼主]2007-04-29 21:29 | Anytao      
@Siwei
谢谢,你所说的的确如此,我们应该关注不太注意的知识点
  回复  引用  查看    
#19楼 [楼主]2007-04-29 21:30 | Anytao      
@Daniel Pang
谢谢,下回也精彩,:-)
  回复  引用  查看    
#20楼 2007-05-01 09:13 | Boler Guo      
标记一下,回头看看
  回复  引用    
#21楼 2007-05-01 10:18 | 双眼皮 [未注册用户]
涛,你越来越强了!我再回北京恐怕难以再和你讨论,你开篇的问题我回答的不好!这篇文章确实是好文!我转到我的博客上了。
  回复  引用  查看    
#22楼 2007-05-05 00:52 | 玩玩      
不错不错。
文章都收藏了哦。
  回复  引用  查看    
#23楼 [楼主]2007-05-08 09:55 | Anytao      
@Boler Guo
@玩玩
呵呵,欢迎常来看看

  回复  引用  查看    
#24楼 [楼主]2007-05-08 09:55 | Anytao      
@双眼皮
别这么谦虚,快点回来吧,我们的论战还要继续,又有新东西和你分享了。
  回复  引用    
#25楼 2007-05-08 15:34 | coolyhtao [未注册用户]
绝顶好文,这方面的论述真的很少,作为初入行的人,正是万分感谢您,期待后续文章,向您学习,致敬!
  回复  引用  查看    
#26楼 [楼主]2007-05-09 15:10 | Anytao      
@coolyhtao
呵呵,感谢支持,继续努力,下回即将推出
  回复  引用  查看    
#27楼 2007-05-24 15:55 | 尧尧      
不错的文章,检测没通过,汗一个
  回复  引用  查看    
#28楼 [楼主]2007-05-24 23:54 | Anytao      
@尧尧
我们一起努力...
  回复  引用  查看    
#29楼 [楼主]2007-06-01 18:06 | Anytao      
@念时
感谢支持。
  回复  引用    
#30楼 2007-06-28 11:36 | vice [未注册用户]
没说的,支持先!继续关注
  回复  引用    
#31楼 2007-07-11 15:13 | pinty [未注册用户]
加油`出书我一定买#1
  回复  引用  查看    
#32楼 [楼主]2007-07-15 22:26 | Anytao      
@pinty
呵呵,持续努力中。。。
  回复  引用    
#33楼 2007-07-18 08:29 | 剑在上海^^ [未注册用户]
值类型是执行initobj指令。功能是将位于指定地址的对象的所有字段 初始化为空引用或适当的基元类型的0 。然后该实例可用于要被调用的构造函数,与Newobj不同,构造函数不是由initobj调用的
  回复  引用  查看    
#34楼 [楼主]2007-07-23 17:22 | Anytao      
@剑在上海^^
及是.
  回复  引用  查看    
#35楼 2007-08-20 21:39 | flyingchen      
太好了,都不好意思不顶下!
  回复  引用    
#36楼 2007-09-14 09:54 | zhoucloud [未注册用户]
@anytao 你貌似忘记解释第一点“new一个class对象和new一个struct或者enum有什么不同? ”中,enum有什么不同了。
文章写的有点深度,有些地方还需要再看一遍,谢谢分享。
  回复  引用  查看    
#37楼 2007-09-20 15:33 | 最后一滴血      
墙角的那块地我占了!!!!
  回复  引用  查看    
#38楼 2007-09-22 08:37 | 镜涛      
虽然零散的看过关于new的理解,不过都不是很全面。楼主的文章很值得阅读,谢谢分享。呵呵
  回复  引用  查看    
#39楼 [楼主]2007-09-26 11:26 | Anytao      
@flyingchen
没说的,感谢支持。
  回复  引用  查看    
#40楼 [楼主]2007-09-26 11:27 | Anytao      
@zhoucloud
关于枚举,实在需要另起篇幅来讨论,很值得挖掘,所以在系列的后续将有讨论,敬请关注。
  回复  引用  查看    
#41楼 [楼主]2007-09-26 11:27 | Anytao      
@最后一滴血
:-)
  回复  引用  查看    
#42楼 [楼主]2007-09-26 11:29 | Anytao      
@镜涛
我还打算再来一篇《再把new说透》,因为最近发现有些理解还有更多值得探讨的东西,原来对技术的把握是不断在领悟,后文应用会有篇再次讨论这个话题。
  回复  引用    
#43楼 2007-10-13 09:05 | MicEnter [未注册用户]
您好:Anytao
在第一个例子中。为什么用 
IntNumber intNum = new IntNumber();
 intNum.ShowInfo();
怎么不会执行IntNumber类的ShowInfo()方法?
  回复  引用  查看    
#44楼 [楼主]2007-10-14 13:34 | Anytao      
@MicEnter
首先,必须澄清的是:
IntNumber intNum = new IntNumber();
intNum.ShowInfo();
执行的就是IntNumber类的ShowInfo()方法。

其次,我理解你的意思应该是:
Number number = new IntNumber();
number.ShowInfo();
怎么不会执行IntNumber类的ShowInfo()方法?

然后,给出我对于这个问题的理解:
ShowInfo方法在基类Number中已经有定义,但不是虚方法,所以子类方法中如果定义了同样的名称的方法,从设计者的角度来看,这样做的目的不是重写父类的ShowInfo,因为不是虚方法。所以,只能是表示该方法ShowInfo是区别于父类的方法,你可以理解为ShowInfo2,虽然同名但是在编译器看来这两个方法是完全不同的两个方法。因此父类对象number在调用ShowInfo时,它调用的显然是Number::ShowInfo(),而子类对象intNumber在调用ShowInfo时,调用的就是IntNumber::ShowInfo()。
这就是new关键字作为隐藏基类方法时的作用。.NET默认就是提供了new的,因此在子类中如果没有显式指明,会给出警告同时编译器会自动加上new来隐藏基类的同名方法。
另外,如果在基类的ShowInfo被定义为virtual,而在子类中如果想隐藏而不是覆写父类方法,则还是使用new关键字来实现,并且执行结果是同样的,你可以试试看。

关于父类、子类的继承机制是如何实现的,请参阅我的另一篇拙作《继承本质论》,相信能够给你更清晰的解释:
http://www.cnblogs.com/anytao/archive/2007/09/10/must_net_15.html

谢谢你的讨论。

  回复  引用    
#45楼 2007-11-08 14:00 | sduhzone [未注册用户]
朋友推荐的blog 看了后感觉写得很好顶了
class MyGenericTester
{
public static void Main(string[] args)
{
Genericer<MyCls> MyGen = new Genericer<MyCls>();
Console.WriteLine(MyGen.GetItem().Name);
}
}
这里 MyGen.GetItem().Name笔误了吧 呵呵 没有定义Name属性
  回复  引用  查看    
#46楼 [楼主]2007-11-08 14:29 | Anytao      
@sduhzone
所言甚是,漏了贴,及时修改:-)
感谢你的指正和关注。
  回复  引用  查看    
#47楼 2007-12-08 15:57 | Edison.Feng      
我明白了
  回复  引用  查看    
#48楼 [楼主]2007-12-08 16:26 | Anytao      
@Edison.Feng
:-)
  回复  引用  查看    
#49楼 2007-12-24 23:22 | 小名阿铁      
哎,看了C++的深度探索对象模型之后........
  回复  引用  查看    
#50楼 [楼主]2007-12-25 09:18 | Anytao      
@小名阿铁
??
  回复  引用    
#51楼 2008-01-14 11:10 | redfox88888 [未注册用户]
楼主,你谈到了new int() 那这个int 究竟是什么东东呢?是类还是结构呢?还是其它什么东西呢?
  回复  引用  查看    
#52楼 [楼主]2008-01-14 13:05 | Anytao      
@redfox88888
你关注的其实是int,而不是new,int是典型的值类型,其内存分配过程详细见:
http://www.cnblogs.com/anytao/archive/2007/12/03/must_net_18.html

http://www.cnblogs.com/anytao/archive/2007/05/23/756401.html

  回复  引用    
#53楼 2008-01-16 16:35 | bluetiger [未注册用户]
"实现派生类中隐藏方法,则基类方法必须定义为virtual"
这句不是很明白.
  回复  引用  查看    
#54楼 [楼主]2008-01-17 10:12 | Anytao      
@bluetiger
这句话,本质上是基于继承层次的版本控制而言的,将基类方法实现为virtual,有利于版本的向前发展和向后兼容,在子类中通过new或者override实现对其的覆写或隐藏。因此,从版本控制的角度而言,将基类方法实现为virtual是必须的。
这里表述不是很明白,抱歉:-)
  回复  引用    
#55楼 2008-04-02 23:12 | zohong [未注册用户]
楼主,感谢你提供了这么多好东西,让我这个初学者学到很多,谢谢楼主!
  回复  引用  查看    
#56楼 [楼主]2008-04-03 09:08 | Anytao      
@zohong
呵呵,也谢谢你的阅读和讨论,欢迎常来:-)
  回复  引用  查看    
#57楼 2008-04-11 13:20 | 风吹屁屁凉      
飘过,实在是 太喜欢lz的文章了


  回复  引用  查看    
#58楼 [楼主]2008-04-11 13:41 | Anytao      
@风吹屁屁凉
呵呵,好名字:-)
谢谢你的关注,还有更多精彩,详见:
http://insidedotnet.cnblogs.com/
http://www.cnblogs.com/anytao/archive/2007/09/14/must_net_catalog.html
http://book.anytao.com/
  回复  引用  查看    
#59楼 2008-04-22 11:20 | 镜涛      
重读,印象更深刻了!
  回复  引用  查看    
#60楼 [楼主]2008-04-22 23:19 | Anytao      
@镜涛
欢迎常来:-)
  回复  引用  查看