解析C#类中的构造函数

《解析C#类中的构造函数》

一.  C#中的构造函数概述:

C#中类包含数据成员和函数成员。函数成员提供了操作类中数据的某些功能,包括方法、属性、构造器和终结器、运算符和索引器。

  1. 构造函数设计的定义

   构造器是允许将类型的实例初始化为良好状态的一种特殊方法。

  1. 2.    构造函数设计的作用:

c#中创建一个引用类型的实例时,首先为实例的数据字段分配内存,然后初始化对象的附加字段(类型对象指针和同步块索引),最后调用类型的实例构造器来设置对象的初始化状态。

  1. 3.    对类没有定义任何共有的或受保护的构造器,导致该类不能使用new在外部代码中实例化,此方式的作用:

     (1).类仅用作某些静态成员或属性的容器,因此永远不会实例化它。

(2).希望类仅通过调用某个静态成员函数来实例化(对象实例化工厂方法)

  4.基类构造函数的使用:

  编译器在调用基类的构造器前,会初始化任何使用了简化语法的字段,以维持源代码给人留下的“这些字段总是有一个值”的印象,如果基类的构造器调用了一个虚方法,它回调派生类中定义的一个方法,就可能出现问题。在这种情况下,用简化语法来初始化的字段在虚方法调用之前就已经初始化了。

二.  C#类中构造函数的分类:

  1. 1.    实例构造函数:

(1).实例构造函数概述:

声明一个与包含的类同名的方法,该方法没有返回类型。【如果没有提供任何构造函数,编译器会在后台创建一个默认的构造函数】

(2).实例构造函数的作用:

A.把所有的成员字段初始化为标准的默认值。

B. 构造引用类型的对象时,在调用类型的实例构造器之前,为对象分配的内存总是先被归零,构造器没有显示重写的所有字段保证都有一个0或null值。实例构造器永远不能被继承。

C. 为了使代码“可验证”,类的实例构造器在访问从基类集成的任何字段之前,必须先调用基类的构造器。如果派生类的构造器没有显示调用一个基类构造器,C#编译器会自动生成对默认的基类构造器的调用。

  1. 2.    静态构造函数:

(1).静态构造函数概述:

静态构造函数(类型构造器、类型初始化构造器):静态构造函数(类型构造器)不能含有参数,没有修饰符,只能编写无参数的构造函数,这种构造函数只能执行一次,一个类也只能有一个静态构造函数,当前面的构造函数是实例构造函数,只要创建类的对象,就会执行它。

 (2).静态构造函数运行原理:

.NET运行库没有确保什么时候执行静态构造函数,所以不应该把要求在某个特定时刻执行的代码放在静态构造函数中。也不能预计不同类的静态构造函数按照什么顺序执行。

(3).静态构造函数的调用:

静态构造函数没有访问修饰符,其他C#代码从来不调用它,但是在类加载时,总是又.NET运行库调用它。静态构造函数只能访问类的静态成员,不能访问类的实例成员。

 3.构造函数使用范围:

    (1).无参数的实例构造函数与静态构造函数可以在同一类中同时定义,有参的实例构造函数与静态构造函数也可以在同一类中同时定义。【在类加载时执行静态构造函数,而在创建实例时执行实例构造函数】

    (2). C#编译器不允许值类型(结构)定义无参数的构造器,所以编译器永远不会生成自动调用它的代码,没有无参数构造器,值类型(结构)的字段总是被初始化为0或null。【C#不允许值类型(结构)定义无参数的构造器,但是CLR允许。C#运行值类型(结构)定义有参数的构造器】

三.  C#类中替代构造函数的方式:

1.在极少数情况下,可以在不调用实例构造器的前提下创建一个类型的实例。使用Object的MemberwiseClone方法,该方法的作用是分配内存,初始化对象的附加字段(类型对象指针和同步块索引),然后将源对象的字节数据复制到新对象中。

2.在用运行时序列化器反序列化对象时,通常也不需要调用构造器,反序列化使用FormatterService类型的GetUninitialzedObject或者GetSafeUninitializedObject方法为对象分配内存,期间不会调用一个构造器。

四.  C#类中构造器的使用情形:

1.静态构造函数的使用:

     类中有一些静态字段或属性,需要在第一次使用类之前,从外部源中初始化这些静态字段和属性。

2.构造函数的调用:

在C#类中,构造器可以调用其他构造器。C#构造函数初始化器可以包含对同一类的另一个构造函数的调用,也可以包含对直接基类的构造函数的调用。初始化器中不能有多个调用。【使用this关键字实现初始化器,调用参数最匹配的那个构造器】构造函数初始化器在构造函数的函数体之前进行。

五.  C#类中类型构造器的性能:

   1.     类型构造器的调用:

JIT编译器必须决定是否生成代码来调用它,而CLR必须确保对它的调用是线程安全的。

  1. 2.     JIT编译器在生成一个对类型构造器的调用,这个调用添加的位置情况:

(1).JIT编译器可以刚好在创建类型的第一个实例之前,或者刚好在访问类的一个非继承的字段或成员之前生成这个调用。即为精确语义。

(2).JIT编译器可以在首次访问一个静态字段或者一个静态/实例方法之前,或者在调用一个实例构造器之前,随便找一个时间生成调用。即为字段初始化前语义。

   3. .JIT对调用的位置选择:

“字段初始化前”语义是首选的,因为它使CLR能够自由选择调用类型构造器的时机,而CLR会尽可能地利用这一点来生成运行得更快的代码。

4.C#编译器如果看到一个类(BeforeFieldInit)包含进行了内联初始化的静态字段,会在类的类型定义表中生成一个添加了BeforeFiledInit元数据标记的记录项。C#编译器如果看到一个类包含显示的类型构造器,就不会添加BeforeFiledInit元数据标记。【静态字段只要在访问之前初始化就可以了,具体什么时间无所谓。而显式类型构造器可能包含具有副作用的代码,所以需要在精确拿捏运行的时间】 

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏吴伟祥

Java类初始化顺序 转

关于构造函数,以下几点要注意: 1.对象一建立,就会调用与之相应的构造函数,也就是说,不建立对象,构造函数时不会运行的。 2.构造函数的作用是用于给对象进行...

723
来自专栏猿人谷

数组与指针

一、数组        数组是由类型名、标识符和维数组成的复合数据类型,类型名规定了存放在数组中的元素类型,维数则指定数组中包含的元素个数。        数组...

1858
来自专栏landv

Java基本语法

1221
来自专栏前端小叙

js数组push方法使用注意

js 数组的push方法,想必大家都知道是向数组末尾添加元素,但是有一个很关键的点需注意: 引自 MDN 返回值 当调用该方法时,新的 length 属性值将被...

2646
来自专栏LinkedBear的个人空间

唠唠SE的集合-04——ArrayList 原

        ArrayList底层维护了一个Object[] 用于存储对象,默认数组的长度是10。

966
来自专栏章鱼的慢慢技术路

统计计算学生成绩类问题汇总

1314
来自专栏GreenLeaves

C# new关键字和对象类型转换(双括号、is操作符、as操作符)

一、new关键字 CLR要求所有的对象都通过new来创建,代码如下: Object obj=new Object(); 以下是new操作符做的事情 1、计算类型...

1939
来自专栏抠抠空间

函数 (一) 基础

一、函数的作用 函数可以让我们代码结构更清晰,而且避免了代码的重复,冗余,使一段代码或者功能可以反复的被调用,大大提高了开发效率 二、函数的定义 def 函数名...

2736
来自专栏大前端_Web

深入解析js中基本数据类型与引用类型,函数参数传递的区别

版权声明:本文为吴孔云博客原创文章,转载请注明出处并带上链接,谢谢。 https://blog.csdn.net/wkyseo/articl...

904
来自专栏java初学

static关键字

27510

扫码关注云+社区