构造函数
构造函数分为:实例构造函数,静态构造函数,私有构造函数。
实例构造函数
1、构造函数的名字与类名相同。
2、使用 new 表达式创建类的对象或者结构(例如int)时,会调用其构造函数。并且通常初始化新对象的数据成员。
3、除非类是静态的,否则会为没有构造函数的类,自动生成一个默认构造函数,并使用默认值来初始化对象字段。
4、构造函数可以有参数,可以以多态的形式存在多个构造函数。
例:
class CoOrds { public int x, y; // 实例构造函数(默认构造函数) public CoOrds() { x = 0; y = 0; } // 具有两个参数的构造函数 public CoOrds(int x, int y) { this.x = x; this.y = y; } // 重写toString方法 public override string ToString() { return (String.Format("({0},{1})", x, y)); } static void Main(string[] args) { CoOrds p1 = new CoOrds(); CoOrds p2 = new CoOrds(5, 3); // 使用重写ToString方法显示结果 Console.WriteLine("CoOrds #1 at {0}", p1); Console.WriteLine("CoOrds #2 at {0}", p2); Console.ReadKey(); } } /* Output: CoOrds #1 at (0,0) CoOrds #2 at (5,3) */
其中CoOrds()是构造函数,诸如此类不带参数的构造函数称为“默认构造函数”。
CoOrds(int x, int y)同样也是构造函数,构造函数可以有参数,允许多态。
静态构造函数
静态构造函数具有以下属性:
-
静态构造函数不使用访问修饰符或不具有参数。
-
在创建第一个实例或引用任何静态成员之前,将自动调用静态构造函数以初始化类。
-
不能直接调用静态构造函数。
-
用户无法控制在程序中执行静态构造函数的时间。
-
静态构造函数的一种典型用法是在类使用日志文件且将构造函数用于将条目写入到此文件中时使用。
-
静态构造函数对于创建非托管代码的包装类也非常有用,这种情况下构造函数可调用
LoadLibrary
方法。 -
如果静态构造函数引发异常,运行时将不会再次调用该函数,并且类型在程序运行所在的应用程序域的生存期内将保持未初始化。
构造函数与静态构造函数:
class TestClass { public static int x = 0; //构造函数 TestClass() { x = 1; } //静态构造函数 static TestClass() {
//第二步,执行x = 2 x = 2; }
//第一步,程序入口Main最先执行。然后执行public static int x = 0 接着执行静态构造函数。 public static void Main(string[] args) { Console.WriteLine("x:{0}", x); //打印,x = 2 TestClass Test = new TestClass();//第三步执行构造函数,此时x = 1 Console.WriteLine("x:{0}", x); //打印 x = 1 Console.Read(); } }
Main是程序入口,当执行Main的时候,最先执行public static int x = 0
接着执行静态构造函数,此时 x = 2
然后执行Main函数里面的内容,打印 x,此时 x = 2
初始化TestClass,然后会执行构造函数,此时 x = 1
打印 x = 1
那么,在调用某类的静态函数时真正的执行顺序:
1、静态变量 > 静态构造函数 > 静态函数
2、静态变量 > 静态构造函数 > 构造函数
类型实例的完整过程。你需要理解这些操作的顺序,以及对象的默认初始化操作。你要保证在构造的过程中对每个成员变量仅初始化一次。实现这一点最好的方法就是,尽可能的早地进行初始化。
下面就是创建某个类型的第一个实例时,所进行的操作顺序为:
(1)静态变量设置为0
(2)执行静态变量初始化器
(3)执行基类的静态构造函数
(4)执行静态构造函数
(5)实例变量设置为0
(6)执行衯变量初始化器
(7)执行基类中合适的实例构造函数
(8)执行实例构造函数同样类型的第二个以及以后的实例将从第5步开始执行,因为类的构造器仅会执行一次。此外,第6步和第7步将被优化,以便构造函数初始化器使编译器移除重复的指令。
练习题:(core项目下,答案不同)
public class A { public static readonly int x; static A() { x = B.y + 1; } } public class B { public static int y = A.x + 1; public static void Main(string[] args) { Console.WriteLine("x:{0},y:{1}。", A.x, y); Console.ReadLine(); } }
下面公布答案:
详细解答:
1、首先,每一个项目有且只能有一个静态类的Main函数作为入口函数。而入口函数是最先执行的。
2、由于Main函数在B类里面,首先会初始化B类。而类的初始化顺序是:类里的静态变量,然后执行静态构造函数。
3、运行起先执行 public static int y = A.x + 1 这个,执行的时候,会先把 y 初始化为0,然后计算 y 的值。
4、计算 y 的值的时候,调用了 A 的静态变量 x 。所以会先初始化A。
5、初始化A时首先去执行 public static readonly int x ,先把 x 初始化为0。
6、然后执行A的静态构造函数 x = B.y + 1 此时 y 已经初始化为0了。
7、计算得到 x = 1。然后回到 public static int y = A.x + 1 得到 y = 2。
8、然后再执行Main函数的内容。得出结果x=1,y=2
补充: 小鹏Y 提出了 .net core 项目下得出的答案有出入。非常感谢他!以下是 .net core 项目的角度
在第二步计算 x = B.y + 1,B.y 的值是1,不是0。 所以在计算 x = B.y + 1 的时候,x = 2。
最后的结果变成:A.x = 2,y = 1
具体为何这样,还不清楚,此篇文章目前只考虑非 core 项目的情况。
私有构造函数
私有构造函数是一种特殊的实例构造函数。 它通常用于只包含静态成员的类中。 如果类具有一个或多个私有构造函数而没有公共构造函数,则其他类(除嵌套类外)无法创建该类的实例。
public class PrivateConstructor { private PrivateConstructor() { //PrivateTest a = new PrivateTest(); //注释打开会报错,错误信息:不可访问,因为它受保护级别限制。因为私有构造函数无法在类的外面实例化。 } public class PrivateTest { int i; private PrivateTest() { i = 3; } static void Main(string[] args) { PrivateConstructor t = new PrivateConstructor(); //嵌套类允许实例化。 PrivateTest p = new PrivateTest(); //类的内部允许实例化。 Console.WriteLine("i:{0}", p.i); //结果:i:3 Console.Read(); } } }
声明空构造函数可阻止自动生成默认构造函数。 请注意,如果不对构造函数使用访问修饰符,则在默认情况下它仍为私有构造函数。 但是,通常会显式地使用 private 修饰符来清楚地表明该类不能被实例化。
实例:
其中单例模式就用到了私有构造函数的特性来保证类不会被实例化。C# 单例模式
相关文章: http://www.cnblogs.com/michaelxu/archive/2007/03/29/693401.html
https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/classes-and-structs/constructors