一、构造方法

  类的构造方法是类的成员方法的一种,它的作用是对类中的成员进行初始化操作。类的构造方法分为:

    1.静态构造方法

    2.实例构造方法

    3.私有构造方法

  1.静态构造方法

  类的静态构造方法是类的成员方法的一种,它的作用是对类中的静态成员进行初始化操作。下面请看代码实例:

 using System;
namespace LycheeTest {
class Test {
//定义一个静态成员变量
private static int a;
//定义静态构造函数
static Test() {
//初始化静态成员变量
a = ;
}
public void Show() {
Console.WriteLine("静态字段 a 的值是:{0}", a); }
}
class Program {
static void Main(string[] args) {
Test t = new Test();
t.Show();
Console.ReadKey();
}
}
}

  首先,上面这段代码定义了两个类。第 3 行代码定义了类 Test。定义类的时候,类的访问权限修饰符有两个,一个是 public,另一个是 internal。当不写任何访问修饰符的时候,类的访问权限默认是 internal。 这个访问权限的意义是,这个类只能被本程序集访问,不能被本程序集以外的类访问。如果这个类是属于类库的,那么它必须是 public 的,否则调用它的程序集就不能访问它。第 5 行代码定义了一个静态字段成员,第 7 行代码就是静态构造方法。可以看到,静态构造方法的特点是,以 static 关键字说明这个方法是静态的,方法名称要和类名完全相同,这里要注意大小写。静态构造方法不能含有参数,静态构造方法不能有返回值。在静态构造方法体内可以做初始化静态成员的操作。第 11 行代码定义了一个实例方法,它的作用是输出静态字段的值。在第 16 行的类 Program 中的 Main(注意:在java中是main) 方法中调用了这个类,第 18行创建了这个类的一个实例,第 19 行 调用了类的实例方法。

  从上面的代码中可以看到,并没有显式调用类的静态构造方法。

  下面请看以上代码的执行结果:

 静态字段 a 的值是:  

  可以看到,静态构造方法确实是被执行了。那么上例就是静态构造方法的执行条件之一,在类的实例被创建时,类的静态构造方法将被自动调用。

  静态构造方法的调用次序是在静态字段的初始值设定项之后。

  也就是第一步是静态字段的默认值设置,第二步是执行静态字段的初始值设定项,第三步就是调用类的静态构造方法。

  下面将前面的代码实例修改一下,代码如下:

 using System;
namespace LycheeTest{
class Test {
private static int a;
static Test() {
a++;
}
public void Show() {
Console.WriteLine("静态字段 a 的值是:{0}", a); }
}
class Program {
static void Main(string[] args) {
Test t = new Test();
t.Show();
Test t1 = new Test();
t.Show();
Console.ReadKey();
}
}
}

  这段代码将静态构造方法做了修改,在方法体内将静态字段 a 进行自增操作。然后在代码的第 17 行又 创建了一个类的实例,然后再次调用类的实例方法。

  下面看执行结果:

静态字段 a 的值是:
静态字段 a 的值是:

  可以看到,静态字段的值并没有增加。这就是静态构造方法执行的特点,它只执行了一次。当程序集 运行的时候,将会创建一个应用程序域,在一个应用程序域中,类的静态构造方法仅仅执行一次。

  下面再对代码实例进行修改如下:

 using System;
namespace LycheeTest {
class Test {
public static int a;
static Test() {
Console.WriteLine("类的静态构造方法开始执行");
a++;
}
public void Show() {
Console.WriteLine("静态字段 a 的值是:{0}", a);
}
}
class Program {
static void Main(string[] args) {
Console.WriteLine("静态字段 a 的值是:{0}", Test.a);
Console.WriteLine("静态字段 a 的值是:{0}", Test.a);
Console.ReadKey();
}
}
}

  这段代码在类的静态构造方法中打印输出了一行标记,类的静态字段的访问权限也修改为 public,这让它可以在类外被调用。在 Main 方法中两次打印输出了静态字段的值,注意在类外调用类的静态字段需要 使用类名进行引用。

  下面是代码的执行结果:

类的静态构造方法开始执行
静态字段 a 的值是:
静态字段 a 的值是:

  本段代码并没有创建类的实例。在引用类的静态成员之前,类的静态构造方法将被调用。这个被调用的类的静态成员包括静态字段和静态方法。这就是类的静态构造方法调用的第二个条件。

  下面再对代码实例进行修改如下:

 using System;
namespace LycheeTest {
class Program {
private static int a;
static Program() {
Console.WriteLine("类的静态构造方法被调用");
a = ;
}
static void Main(string[] args) {
Console.WriteLine("Main 方法被调用");
Console.WriteLine("静态字段 a 的值是:{0}", a);
Console.ReadKey();
}
}
}

  这段代码在包含 Main 方法的类中定义了静态字段和静态构造方法。因为 Main 方法也是一个静态方法,类的静态构造方法被调用而且它是类的入口点方法,那么它和类的静态构造方法之间是谁先调用呢?下面首先来看代码的执行结果:

类的静态构造方法被调用
Main 方法被调用
静态字段 a 的值是:

  通过代码的执行结果可以看到,因为类的入口点方法仍然是一个静态方法,那么在任何静态成员被调用之 前,静态构造方法都首先被调用。所以,可以得出如下结论,类的静态构造方法先于类的 Main 方法被调用。

  那么类的静态构造方法能否被显式调用呢?下面看代码实例:

 using System;
namespace LycheeTest {
class Program {
private static int a;
static Program() {
Console.WriteLine("类的静态构造方法被调用");
a = ;
}
static void Main(string[] args) {
Program();
Console.ReadKey();
}
}
}

  在这段代码中的第 10 行显式调用了类的静态构造方法,这时编译器会报错。

  2.实例构造函数

  类的实例构造方法是类的成员方法的一种,它的作用是对类的实例成员进行初始化操作。实例构造方法可以实现重载,在创建类的实例时,可以显式的指定不同的参数来调用重载的不同的实例构造方法。下面请看代码实例:

 using System;
namespace LycheeTest {
class Program {
private static int a;
private int b = ;
private string c = "Hello World"; static Program() {
Console.WriteLine("类的静态构造方法被调用");
a = ;
}
public Program(int a, string s) {
Console.WriteLine("带二个参数的构造方法被调用");
this.b = a;
this.c = s;
}
public Program(int a) : this(a, "通过 this 关键字调用构造方法") {
Console.WriteLine("带一个参数的构造方法被调用");
}
public void Show() {
Console.WriteLine("静态字段 a 的值是:{0}", a);
Console.WriteLine("实例字段 b 的值是:{0}", b);
Console.WriteLine("实例字段 c 的值是:{0}", c);
}
static void Main(string[] args) {
Program p1 = new Program(, "这是创建的实例 P1");
Program p2 = new Program();
p1.Show();
p2.Show();
Console.ReadKey();
}
}

  这段代码的第 4 行、第 5 行和第 6 行分别定义了三个字段成员,第 4 行是静态字段,第 5 行和第 6 行代码都有初始值设定项。代码的第 8 行就是一个实例构造方法的定义,实例构造方法也是以类名作为方法名,它没有返回值, 在方法名前面是访问权限修饰符,可以使用的访问权限修饰符包括 public、private 和 protected。其中的 protected 意味着构造方法只能在此类内部访问。实例构造方法可以带参数。 第 12 行代码的实例构造方法使用两个传入的参数对实例字段进行了赋值。第 17 行代码定义了带一个参数的实例构造方法,它和前一个实例构造方法形成了重载。实例构造方法可以通过 this 关键字调用其他的实例构造方法,方法就是在参数列表的后面使用冒号然后接 this 关键字, 然后再跟参数列表,这个参数列表要匹配另一个重载的实例构造方法。第 17 行的构造方法只有一个参数, 它将这个参数通过 this 关键字传递给了另一个构造方法,在用 this 调用另一个构造方法的时候,为其同时传入了一个字符串参数。第 24 行的实例方法打印类的字段成员的值。在 Main 方法中,第 26 行代码和第 27 行代码分别定义了两个实例,它们使用 new 关键字调用了不同的实例构造方法。第 28 行和第 29 行分别调用实例方法打印类的静态字段和实例的两个字段成员的值。

  下面先来看代码的执行结果:

类的静态构造方法被调用 带二个参数的构造方法被调用 带二个参数的构造方法被调用 带一个参数的构造方法被调用 
静态字段 a 的值是:
实例字段 b 的值是:
实例字段 c 的值是:这是创建的实例 P1 静态字段 a 的值是:
实例字段 b 的值是:
实例字段 c 的值是:通过 this 关键字调用构造方法

  现在用执行结果来介绍实例构造方法的执行过程,当第 26 行代码创建类的实例时,类的静态字段首先被设置成默认值,因为没有字段的初始值设定项,所以接着就执行类的静态构造方法。这时静态字段 a 被 设置成 11。因为第 26 行代码使用 new 调用了带有两个参数的实例构造方法,所以首先实例字段 b 被设置为 0,实例字段 c 被设置为 null。然后执行字段的初始值设定项,b 被赋值为 12,c 被赋值为“Hello World”。接 下来执行实例构造方法体中的第一个语句,“带二个参数的构造方法被调用”这个字符串被打印。接下来 实例 p1 的字段 b 被设置为传入的参数 33,注意构造方法的形参 a 在这里覆盖了类的静态字段 a。也就是说, 这时起作用的是实例构造方法的局部变量 a。然后实例字段 c 被设置为字符串"这是创建的实例 P1"。第 27 行代码使用 new 关键字调用了带一个参数的实例构造方法,在调用时,首先属于 p2 的实例字段 b 被设置为 0,实例字段 c 被设置为 null。然后执行字段的初始值设定项,b 被赋值为 12,c 被赋值为“Hello World”。接下来执行的是 this 引用的带两个参数的实例构造方法,"带二个参数的构造方法被调用"这个 字符串被打印。然后 b 被设置为 34,c 被设置为"通过 this 关键字调用构造方法"。最后,代码控制又返回 来执行带一个参数的实例构造方法体中的打印语句,"带一个参数的构造方法被调用"这个字符串被打印。 至此,实例构造方法的执行完毕。接下来的代码打印静态字段的值,可以看到两个实例打印出来的静态字段值是一样的,但是它们的实 例字段的值各不相同。

  可选参数和命名参数也可以用于实例构造方法,下面看代码实例:

 using System;
namespace LycheeTest {
class Program {
private int b;
private string c;
public Program(int a = , string s = "") {
this.b = a;
this.c = s;
}
public void Show() {
Console.WriteLine("实例字段 b 的值是:{0}", b);
Console.WriteLine("实例字段 c 的值是:{0}", c);
}
static void Main(string[] args) {
Program p1 = new Program(); //构造方法的两个参数都采用默认值
Program p2 = new Program(); //构造方法的 string 类型参数采用默认值
Program p3 = new Program(, "Hello World"); //构造方法的两个参数采用传入参数
Program p4 = new Program(s: "今天的天气真好"); //采用命名参数,另一个参数 a 采用默认值
p1.Show();
p2.Show();
p3.Show();
p4.Show();
Console.ReadKey();
}
}
}

  代码的第 6 行定义了一个带有可选参数和命名参数的构造方法,然后第 15 创建了一个类的实例,在构造方法中没有传入任何参数,这时,构造方法的两个参数都采用默认值。第 16 行代码为构造方法传入了一个 int 类型的参数,这时,另一个 string 类型的参数采用默认值。 第 17 行代码传入了两个参数,构造方法的两个参数都使用了这两个传入的参数。第 18 行代码使用了命名参数指定传入的参数是 string 类型的参数,并将它传递给形参 s。这时另一 个 int 类型的参数采用默认值。第 19 行到第 23 行代码打印类的实例字段的值。这段代码的执行结果如下:

实例字段 b 的值是:
实例字段 c 的值是:
实例字段 b 的值是:
实例字段 c 的值是:
实例字段 b 的值是:
实例字段 c 的值是:Hello World 实例字段 b 的值是:
实例字段 c 的值是:今天的天气真好

3.私有构造函数

   私有构造方法就是访问权限修饰符为 private 的实例构造方法。将访问权限修饰符设置为 private 有 两个目的,一是它不能被继承;二是它不能实例化。所以,使用私有构造方法通常用来设计工具类,即这 个类通过一些静态成员来完成某些操作,而不必要实例化这个类。下面是代码实例:

 using System;
namespace LycheeTest {
class Test {
private Test() {}
private const float PI = 3.14159265358979F;
public static float Area(float r) {
return PI * r * r;
}
}
class Program {
static void Main(string[] args) {
Console.WriteLine("请输入圆的半径:");
float r = Convert.ToSingle(Console.ReadLine());
float area = Test.Area(r);
Console.WriteLine("圆的面积为:{0}", area);
Console.ReadKey();
}
}
}

第 4 行代码简单地定义了一个 private 的无参构造方法就将类定义成了一个无法实例化的类。第 5 行定义了一个常量 PI,第 6 行定义了一个静态的方法 Area 来计算圆的面积,这个方法要求传入 一个 float 类型的圆的半径。第 13 行代码将用户输入的字符串转化成 float 类型的变量,第 14 行代码调用类 Test 的静态方法来求 圆的面积,并将结果赋值给了变量 area。第 15 行打印输出圆的面积。可以看到,求圆的面积没有必要实例化类,而只需要类的静态方法的功能就可以了。可以说,项目需要的就是一个工具类,在这种情况下,定义一个私有构造方法就可以解决问题。

c#核心基础--类的构造方法的更多相关文章

  1. C#核心基础--类(2)

    C#核心基础--类的声明 类是使用关键字 class 声明的,如下面的示例所示: 访问修饰符 class 类名 { //类成员: // Methods, properties, fields, eve ...

  2. C#核心基础--类的声明

    C#核心基础--类的声明 类是使用关键字 class 声明的,如下面的示例所示: 访问修饰符 class 类名 { //类成员: // Methods, properties, fields, eve ...

  3. C#核心基础--类的继承

    继承 一个类可以继承自另一个类.在 C#中,类与类之间只存在单一继承.也就是说,一个类的直接基类只能有一个.当类与类之间实现继承的时候,子类可以将它的直接基类的所有成员当做自己的成员,除了类的静态构造 ...

  4. css核心基础总结篇

    今日这篇是整合前面的css补充知识的. 我觉得前面的关于css的知识补充进去有点乱,今日整理整理一下. 层叠样式表 层叠是什么意思?为什么这个词如此重要,以至于要出现在它的名称里. 层叠可以简单地理解 ...

  5. java中的反射机制,以及如何通过反射获取一个类的构造方法 ,成员变量,方法,详细。。

    首先先说一下类的加载,流程.只有明确了类这个对象的存在才可以更好的理解反射的原因,以及反射的机制. 一.  类的加载 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三 ...

  6. Android应用的核心基础

    Android4开发入门经典 之 第二部分:Android应用的核心基础 Android应用中的组件 Application Components Android应用中最主要的组件是: 1:Activ ...

  7. Servlet---JavaWeb技术的核心基础,JavaWeb框架的基石(一)

    初学JavaWeb开发,请远离各种框架,从Servlet开始.         Web框架是开发者在使用某种语言编写Web应用服务端是关于架构的最佳实践.很多Web框架是从实际的Web项目抽取出来的, ...

  8. JavaScript编程:javaScript核心基础语法

    1.javaScript核心基础语法: javaScript技术体系包含了5个内容:          1.核心语言定义:          2.原生对象和雷子对象:          3.浏览器对象 ...

  9. Python基础-类

    Python基础-类 @(Python)[python, python基础] 写在前面 如非特别说明,下文均基于Python3 摘要 本文重点讲述如何创建和使用Python类,绑定方法与非绑定方法的区 ...

随机推荐

  1. C#.Net Core 操作Docker中的redis数据库

    做软件开发的人,会在本机安装很多开发时要用到的软件,比如数据库,有MS SQL Server,MySQL,等,如果每种数据库都按照在本机确实有点乱,这个时候我们就想用虚拟机来隔离,这样就不会扰乱本机一 ...

  2. ES6躬行记(2)——扩展运算符和剩余参数

    扩展运算符(Spread Operator)和剩余参数(Rest Parameter)的写法相同,都是在变量或字面量之前加三个点(...),并且只能用于包含Symbol.iterator属性的可迭代对 ...

  3. Docker容器互访三种方式

    我们都知道docker容器之间是互相隔离的,不能互相访问,但如果有些依赖关系的服务要怎么办呢.下面介绍三种方法解决容器互访问题. 方式一.虚拟ip访问 安装docker时,docker会默认创建一个内 ...

  4. date、sleep和usleep命令

    bash&shell系列文章:http://www.cnblogs.com/f-ck-need-u/p/7048359.html 1.1 date命令 date用于获取和设置操作系统的时间,还 ...

  5. 为Linux配置常用源:epel和IUS

    CentOS上,除了os类的yum源,还需要配置几个常用的源:epel.ius. 有很多国内很多镜像站点都提供了各类仓库的镜像站点,个人感觉比较全的是阿里云http://mirrors.aliyun. ...

  6. DNS域名解析之搭建公司内部域--技术流ken

    什么是DNS DNS( Domain Name System)是“域名系统”的英文缩写,是一种组织成域层次结构的计算机和网络服务命名系统,它用于TCP/IP网络,它所提供的服务是用来将主机名和域名转换 ...

  7. 前端(一)之 HTML

    前端之 HTML 前言 python 基础.网络编程.并发编程与数据库要开始告一段落了,从现在开始进入前端的学习.前端的东西多且杂,需要好好地练习. 什么是前端 前端即网站前台部分,运行在 PC 端, ...

  8. linux的文档和目录结构

    在Linux底下,所有的文件与目录都是由根目录开始,是目录与文件的源头,然后一个个的分支下来,如同树枝状,因此称为这种目录配置为:目录树. 目录树的特点是什么呢? 目录树的起始点是根目录(/,root ...

  9. IdentityServer4 中文文档 -11- (快速入门)添加基于 OpenID Connect 的用户认证

    IdentityServer4 中文文档 -11- (快速入门)添加基于 OpenID Connect 的用户认证 原文:http://docs.identityserver.io/en/releas ...

  10. 【转】探讨:ASP.NET技术的学习顺序问题

    摘要:很多人对于ASP.NET的入门和学习顺序比较迷茫,今天让我们一起来跟随作者的思路学习探讨ASP.NET的学习顺序问题,希望有所帮助. 如果你已经有较多的面向对象开发经验,跳过以下这两步: 第一步 ...