最近忙着看新还珠,好几天不学习了。玩物丧志啊,罪过罪过。

今天总结的是类构造器的知识,其实这方面的文章蛮多的,可还是觉得亲自写一下对自己的思考和认识会有提高。


对于构造器,大家应该都不陌生,它主要是用来进行初始化状态的。包括实例构造器和类构造器两种,先给大家看个实际的例子。

class Dog : Animal
 {

//字段

private string _name;
         private int _age;
         public string Name//属性
         {
           get { return _name; }
           set { _name = value; }
         }
         public int Age
        {
          get { return _age; }
          set { _age = value; }
        }

public static string type = "动物";//静态字段

//实例构造器

public Dog() //①无参实例构造函数
      {
         _name = "无名";
      }
     public Dog(string name) //②重载,带参实例构造函数
     {
         this._name = name;
     }
     public Dog(string name, int age) //③显式调用其他构造函数
      : this()
     {
       _age = age;
     }

static Dog()                      //④类构造函数 (类构造器)                
      {
            type = "狗狗";
      }

}

class Animal//基类
  {
     public Animal()
     {
            Console.WriteLine("我是一只动物。");
      }
 }

一 实例构造器

实例构造器主要负责将类型的实例初始化到一个合理的状态。引用类型和值类型的实例构造器是有所区别的。

1. 引用类型实例构造器

实例构造器可以进行重载,而且可以具有不同的访问限制。上面例子中①②③都是引用类型实例构造器。

实例构造函数和类名相同,但是没有返回类型。在ILDasm.exe中查看为.ctor。

如果我们没有定义实例构造器,那么编译器会为我们默认产生一个无参构造器。

实例对象初始化过程

  • 为实例分配内存;
  • 初始化附加成员,包括方法表指针和SyncBlockIndex变量(我们已经在 跟小静读CLR via C#(03)中已经提到过)。
  • 调用实例构造器进行初始化。

在调用构造函数前,变量被初始化为0或者null,所以没有被构造器改变的变量在实例创建后将保持0值。例如下面的age字段保持0值

调用顺序

如果类没有显示定义构造器,编译器会自动生成一个无参构造器,调用基类的无参构造器。例如

public class Animal{}

相当于

public class Animal

{

public Animal():base(){}

}

如果类的修饰符为static(sealed和abstract),编译器不会默认生成构造器;

如果基类没有提供无参构造器,那么派生类必须显示调用一个构造器,否则编译错误。

如果存在继承关系,派生类在使用基类的字段之前应该先调用基类的构造器。如果派生类没有显式调用基类构造器,则编译器会自动产生调用基类无参构造器的代码,沿着继承层次一直到System.Object的无参构造器位置。例如下面,调用Dog dog=new Dog()方法的结果。

class Dog:Animal。。。

Dog()方法IL代码

代码爆炸?

为了防止构造器重载时大量重复赋值造成代码膨胀,我们建议将公共的初始化语句放在一个构造函数中,然后其他的构造器显式调用该构造器。

2. 值类型实例构造器
  • 值类型没有默认产生的无参构造器,也不允许我们定义无参构造器。但是我们可以自定义带参数的构造器。

  • 不允许在值类型中内联实例字段的初始化。下面的例子会产生编译错误。

    struct TestStruct
        { 
           partial int number=5;
        }
  • 值类型带参构造函数必须对所有实例字段进行初始化才可以。如果有变量没有初始化,就会报错。

如果不想对所有字段一一初始化,有一种替代方案:


  struct Dog
    {
        public int age;
        public string name;
        public Dog(string Name)
        {
            this = new Dog();
            name = Name;
        }
    }

在值类型构造器中,this代表值类型本身的一个实例,用New创建的值类型实例赋给this时,会将所有字段置零。所以这个方案可以编译通过。

  • 带参构造函数定义之后需要用new显式调用才能执行。否则值类型的字段会保持0或Null值。

     

二 类构造器

  • 类构造器适用于引用类型(包括接口)和值类型,用来设置类的初始状态。类中并没有默认产生的类构造器,需要我们显式构造,标记为static方法。在元数据表中对应.cctor

  • 类构造器只能有一个,不能进行重载。而且不能含参数。类构造器的目的是初始化类的静态成员,它只能访问静态成员,不能访问实例成员。
  • 类构造器的访问限制是私有的,但是我们不能在类构造器前添加访问修饰符,private也不行,否则会产生编译错误,这样做是为了防止开发人员调用该方法。它的调用是由CLR负责的,我们应该避免编写需要以特定顺序调用类构造器的代码。
  • 类构造器不要调用其基类的类构造器。因为基类的静态成员并没有被派生类所继承,它只是编译时静态绑定。
  • 类构造器的调用顺序和实例构造器相似的,首先静态字段被初始化,然后在构造其中被重新赋值。例如:

class Dog : Animal

{

public static string type = "动物";//静态字段

//类构造函数

static Dog()

{

type = "狗狗";

}

}

Console.WriteLine(Dog.type);

 本节小测

Dog的age字段值是什么呢?

A.0             B.5          C.其他

最新文章

  1. sublime text快捷键
  2. 关于ddpush推动实现抖动视频的使用
  3. C++ 面向对象的三个特点--多态性(一)
  4. 368. Largest Divisible Subset -- 找出一个数组使得数组内的数能够两两整除
  5. 【风马一族_Java】9*9口诀
  6. [转载]Asp.net MVC2 与 MVC3 路由调试好帮手RouteDebug 与 RouteDebugger
  7. nodejs http.get乱码问题处理方法
  8. HDU1711:Number Sequence
  9. 在调试安卓系统的时候需要这个 ”adb disable-verity“
  10. kubernetes中kubeconfig的用法
  11. cocos creator 动画编辑器以及骨骼动画的使用
  12. [.net 面向对象程序设计深入](31)实战设计模式——使用Ioc模式(控制反转或依赖注入)实现松散耦合设计(1)
  13. Ecplise 快捷键笔记
  14. js 日期比较大小,js判断日期是否在区间内,js判断时间段是否在另外一个时间段内
  15. JDK动态代理和CGLIB代理的区别
  16. Gym 101194E / UVALive 7901 - Ice Cream Tower - [数学+long double][2016 EC-Final Problem E]
  17. 【eclipse】删除工作空间
  18. gulp4小demo
  19. ES(6): access elasticsearch via curl
  20. [UVa10296]Jogging Trails

热门文章

  1. iOS跳转到另一个程序
  2. 使用 Intel HAXM 为 Android 模拟器加速,媲美真机
  3. 160809209_李梦鑫_C语言程序设计实验2+选择结构程序设计_进阶
  4. windows2008安装IIS
  5. VMware vCenter Server安装图解教程
  6. 2016年11月5日--marquee标签、插入百度地图
  7. BZOJ 1670: [Usaco2006 Oct]Building the Moat护城河的挖掘
  8. web图片轮播实现
  9. django中抽象基类的Foreignkey的定义
  10. 转:理解Cookie和Session机制