.Net 基础new关键字的用法

 

一、new的基本功能

一般说来,new关键字在.net中用于以下四个场合。

  1. 作为运算符,用于创建对象和调用构造函数,范围用得最多吧。
  2. 实现多态。
  3. 作为修饰符,用于向基类成员隐藏继承类成员,一般继承情况下,扩展基类方法用得多。
  4. 作为泛型参数约束,用于在泛型声明中约束用作类型参数的参数类型,这个也好像用得多吧。

二、new的基本用法

  1. 先来说说作为泛型参数约束的用法。
MSDN 中的定义是:new约束指定泛型类声明中的任何类型参数都必须有公共的无参构造函数。当泛型类型创建类型的新实例时,将此约束应用于类型参数。
有个注意的地方就是有其他约束时,new约束比须最后指定。
下面实现一个例子:
复制代码
1     class Person<T> where T : new()
2     {
3         public T GetName()
4         {
5             return new T();
6         }
7     }

9     class Boy
10     {
11         private string _name;
12 
13         public string Name
14         {
15             get{return _name;}
16             set { _name = value; }
17         }
18 
19         public Boy()
20         {
21             _name = "feng";
22         }
23     }
24 
25     class Program
26     {
27         static void Main(string[] args)
28         {
29             Person<Boy> MBoy = new Person<Boy>();
30 
31             Boy a = new Boy();
32             Console.WriteLine(a.Name);
33 
34             Console.WriteLine(MBoy.GetName().Name);
35             Console.ReadKey();
36         }
37     }   
复制代码

结果 打印出2个"feng"

 

   2.  再来说说作为修饰符和多态的用法。

作为修饰符,主要用在向基类成员隐藏继承成员。如果要实现派生类隐藏方法,则基类的方法必须定义为virtual,将基类方法实现为virtual可以保证向前扩展和向后兼容。分别在派生类通过override或new进行灵活控制。new和overdide不可同时共存,因为new用于创建一个新成员,同时隐藏基类的同名成员,注意不是覆盖,要想访问基类的成员可以通过base修饰符来访问。而override则不行。

通过new实现多态。

  1. 比如通过基类扩展来实现继承式多态:

    Animal a =new dog();  

    //其中cry()方法实现为虚方法,子类dog继承至Animal 并覆写了该方法,虽然a是父类Animal的一个变量,但是实现的是子类dog的方法,这正是new实现多态的体现吧。

     

        a.cry();

   2.也可以通过继承接口来实现多态:

     IAnimalRun r=new dog();//dog实现了接口IAnimalRun 的 run()方法

     r.run();

      

看下面一个例子来加深理解:

 

 
复制代码

 1     class Num
 2     {
 3         public static int i = 111;
 4 
 5         public virtual void ShowClassInfo()
 6         {
 7             Console.WriteLine("我是基类呵呵");
 8         }
 9         public virtual void ShowNum()
10         {
11             Console.WriteLine("基类的数字是{0}", i.ToString());
12         }
13     }
14 
15     class SubNum : Num
16     {
17         new public static int i = 222;
18 
19         //new 作为修饰符:只是隐藏基类同名方法
20         public new virtual void ShowClassInfo()
21         {
22             Console.WriteLine("我是子类哈哈");
23         }
24 
25         //override则是覆写了基类的方法
26         public override void ShowNum()
27         {
28             Console.WriteLine("子类的数字是{0}", i.ToString());
29         }
30     }
31 
32     class Program
33     {
34         static void Main(string[] args)
35         {
36             Num num1 = new Num();//new 运算符用法
37             num1.ShowNum();
38             SubNum sbnum = new SubNum();
39             //下面2个调用的都是子类的方法 但是实现机制不一样
40             sbnum.ShowNum();//结果:111
41 
42             sbnum.ShowClassInfo();//结果:我是子类
43             
44             //new作为多态的用法:下面是典型的基类继承似多态
45             Num num2 = new SubNum();//num2虽然是基类的变量,但是指向了之类SubNum实例的引用
46 
47             //调用方法时会在运行期检查虚拟方法表,来确定要调用的方法
48             num2.ShowClassInfo();//由于方法只是被隐藏了,没有被重写 所有还是调用的基类的方法
49 
50             num2.ShowNum();//方法被覆盖了 所有调用的是子类的方法
51             Console.ReadKey();
52         }
53     }
复制代码

 个人觉得new实现多态可以放到作为运算符里面不去,不知道你们是不是这样认为的。

 

3.作为运算符的用法。

作为修饰符和约束的情况也许要多加理解,作为运算符一定是一个不难理解的话题,因为大多数时候我们就是用new作为运算符来创建对象或结构体或枚举等等。new运算符用于返回一个引用,指向系统分配的托管堆的内存地址,那new实例化值类型和引用类型有啥区别呢,来看一段简单的代码。

 class MyClass
 2     {
 3         private int _id;
 4         public MyClass(int id)
 5         {
 6             _id = id;
 7         }
 8     }
 9 
10     struct MyStruct
11     {
12         private string _name;
13         public MyStruct(string name)
14         {
15             _name = name;
16         }
17     }
18 
19     class Program
20     {
21         public static void Main()
22         {
23             int i=0;
24             int j = new int();
25             string str = new string('#', 10);
26 
27             MyClass myClass = new MyClass(123);
28 
29             MyStruct myStruct = new MyStruct("hehe");
30             
31             Console.WriteLine(j);
32             Console.ReadKey();
33         }
34     }

 我们来用reflector工具来查看它的IL代码如下:


复制代码
.method public hidebysig static void Main() cil managed
{
    .entrypoint
    .maxstack 3
    .locals init (
        [0] int32 num,//这里我创建的明明是i反编译确是num 不理解,求大家指出
        [1] int32 j,//这里我自己修改成了j
        [2] string str,
        [3] class TestNewFunction.MyClass myClass,
        [4] valuetype TestNewFunction.MyStruct myStruct)
    L_0000: nop 

    //手动初始化i为0

    L_0001: ldc.i4.0 
    L_0002: stloc.0 

    //通过new来初始化j为0 

    L_0003: ldc.i4.0 //j的值为0
    L_0004: stloc.1 

    //调用newobj初始化字符串str 

    L_0005: ldc.i4.s 0x23//'#'的16进制
    L_0007: ldc.i4.s 10//这个10应该是count
    L_0009: newobj instance void [mscorlib]System.String::.ctor(char, int32)

    L_000e: stloc.2 

    //调用newobj创建对象,并调用构造函数初始化 ,内存分配在托管堆上

    L_000f: ldc.i4.s 0x7b//111的十六进制
    L_0011: newobj instance void TestNewFunction.MyClass::.ctor(int32)
    L_0016: stloc.3 

    //加载结构体 

    L_0017: ldloca.s struct2
    L_0019: ldstr "hehe"

    //调用构造函数初始化,内存分配在线程的栈上面 

    L_001e: call instance void TestNewFunction.MyStruct::.ctor(string)
    L_0023: nop 
    L_0024: ldloc.1 
    L_0025: call void [mscorlib]System.Console::WriteLine(int32)
    L_002a: nop 
    L_002b: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
    L_0030: pop 
    L_0031: ret 
}

 

 

 

复制代码

 本人对IL知道的不是很多,有错误希望大家能够提出来。通过以上分析,可以得出一点结论:

  1. new一个class时,先调用newobj命令 来为实例在托管堆中分配内存,然后调用构造函数来进行对象初始化。
  2. new 一个struct 时,直接调用其构造函数完成初始化。
  3. new 一个int时,用于初始化其值为0。

差点忘了 new运算符不可重载,分配内存失败时,会引发OutOfMemoryException异常。

以上是本人对new这个关键字的用法。如有错误欢迎指出。

原文出处:章出处:http://www.cnblogs.com/xingbinggong/archive/2011/07/05/2098454.html

posted @ 2015-01-05 23:55  试着改变  阅读(400)  评论(0编辑  收藏  举报