C#基础 [10] 构造函数
一、构造函数概述
构造函数是在创建给定类型的对象时执行的类方法。构造函数具有与类相同的名称,它是类或结构中第一个被执行的方法,通常用于申请内存、初始化新对象的数据成员。任何时候,只要创建类或结构,就会调用它的构造函数。构造函数可以重载,即类或结构可能有多个接受不同参数的构造函数。构造函数使得程序员可设置默认值、限制实例化以及编写灵活且便于阅读的代码。构造函数也被称为构造器或构造方法。
二、实例构造函数
1.默认构造函数
如果在类声明中没有显式声明构造函数,那么编译器会自动生成一个隐式的默认构造函数。该构造函数的函数名和类名相同、public、没有参数、方法体为空,它实例化对象,并且将成员字段初始化为成员类型的默认值。无论何时,只要使用 new 运算符实例化对象,并且不为 new 提供任何参数,就会调用默认构造函数。下面是一个Person类的声明,没有显式声明构造函数。
1 // 无显式构造函数 2 class Person 3 { 4 // Fields 5 private string _name; 6 7 // Methods 8 /// <summary> 9 /// 设置新名称 10 /// </summary> 11 /// <param name="newName"></param> 12 public void SetName(string newName) 13 { 14 this.Name = newName; 15 } 16 17 // Properties 18 public string Name 19 { 20 get { return _name; } 21 set { _name = value; } 22 } 23 }
下面是编译器为Person类自动生成的默认构造函数:
1 public Person() 2 { 3 }
下面是使用new运算符创建类的实例,它调用了默认的无参构造函数。
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 // 调用默认构造函数创建类对象 6 var p = new Person(); 7 } 8 }
2.显式声明的无参构造函数
显式声明的无参构造函数有如下特征:
第一,构造函数名和类名相同,可以有修饰符。
第二,构造函数无返回值,连void都没有。
第三,可以带有访问修饰符,如果允许从类的外部创建类的实例,则为public。
下面是一个为上面的Person类显示声明的一个public无参构造函数,它为成员字段Name和isInitialized设了初始值:
1 // Constructor 2 public Person() 3 { 4 this.Name = "unknow"; 5 this.isInitialized = true; 6 }
第四,一旦为类显式声明了任何一个构造函数,编译器就不会为该类生成默认的构造函数,这时如果还想调用和默认构造函数一样的无参构造函数,就必须得显式声明,否则编译器会报错。下面为Person类的构造函数加上参数,使得与默认构造函数相同的声明不存在了:
1 // Constructor 2 // 为构造函数声明一个参数,与默认构造函数相同的声明不存在了 3 public Person(string name) 4 { 5 this.Name = name; 6 this.isInitialized = true; 7 }
然后,调用默认构造函数实例化一个对象就会报错:
// 调用默认构造函数创建类对象 var p = new Person();
3.构造函数的参数与重载
构造函数与其他方法一样,可以带有参数,也可以重载。这样就可以根据条件传递不同的参数,实例化对象了。下面为Person类添加了一个Age属性和两个构造函数的重载:
1 class Person 2 { 3 // Fields 4 private string _name; 5 private int _age; 6 7 public bool isInitialized; 8 9 // Constructor 10 // 1.与默认构造函数一致的无参构造函数 11 public Person() 12 { 13 14 } 15 // 2.为构造函数声明一个参数,与默认构造函数相同的声明不存在了 16 public Person(string name) 17 { 18 this.Name = name; 19 this.isInitialized = true; 20 } 21 // 3.构造函数的重载2,有两个参数 22 public Person(string name, int age) 23 { 24 this.Name = name; 25 this.Age = age; 26 } 27 28 // Methods 29 /// <summary> 30 /// 设置新名称 31 /// </summary> 32 /// <param name="newName"></param> 33 public void SetName(string newName) 34 { 35 this.Name = newName; 36 } 37 38 // Properties 39 public string Name 40 { 41 get { return _name; } 42 set { _name = value; } 43 } 44 public int Age 45 { 46 get { return _age; } 47 set { _age = value; } 48 } 49 }
下面分别调用各个构造函数实例化对象:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 // 1.调用默认构造函数创建类对象 6 var p = new Person(); 7 8 // 2.调用一个参数的构造函数创建对象 9 var p1 = new Person("李白"); 10 11 // 3.调用两个参数的构造函数创建对象 12 var p2 = new Person("李白", 18); 13 } 14 }
三、静态构造函数
静态构造函数用于初始化任何 静态数据,或用于执行仅需执行一次的特定操作。在创建第一个实例或引用任何静态成员之前,将自动调用静态构造函数。下面为Person类添加了一个static字段和一个static构造函数:
1 class Person 2 { 3 // Fields 4 private string _name; 5 private int _age; 6 // 静态字段 7 private static string _test; 8 9 public bool isInitialized; 10 11 // Constructor 12 // 1.与默认构造函数一致的无参构造函数 13 public Person() 14 { 15 16 } 17 // 2.为构造函数声明一个参数,与默认构造函数相同的声明不存在了 18 public Person(string name) 19 { 20 this.Name = name; 21 this.isInitialized = true; 22 } 23 // 3.构造函数的重载2,有两个参数 24 public Person(string name, int age) 25 { 26 this.Name = name; 27 this.Age = age; 28 } 29 30 // 静态构造函数 31 static Person() 32 { 33 _test = "test static constructor"; 34 Console.WriteLine("我是静态构造函数,我被调用!"); 35 } 36 37 // Methods 38 /// <summary> 39 /// 设置新名称 40 /// </summary> 41 /// <param name="newName"></param> 42 public void SetName(string newName) 43 { 44 this.Name = newName; 45 } 46 47 // Properties 48 public string Name 49 { 50 get { return _name; } 51 set { _name = value; } 52 } 53 public int Age 54 { 55 get { return _age; } 56 set { _age = value; } 57 } 58 }
静态构造函数有如下特征:
第一,静态构造函数既没有访问修饰符,也没有参数。试图为静态构造函数添加修饰符将得到一条如下图所示的错误消息:
第二, 在程序中,用户无法直接调用静态构造函数;用户无法控制何时执行静态构造函数;在创建第一个实例或引用任何静态成员之前,系统将自动调用静态构造函数来初始化类。试图手动调用构造函数,根本找不到:
第三,即使创建类的多个实例,静态构造函数也仅运行一次,并且在实例构造函数运行之前运行。下面调用构造函数的不同重载版本创建3个Person类的实例:
1 static void Main(string[] args) 2 { 3 // 1.调用默认构造函数创建类对象 4 var p = new Person(); 5 p.Name = "test"; 6 Console.WriteLine(p.Name); 7 8 // 2.调用一个参数的构造函数创建对象 9 var p1 = new Person("李白"); 10 Console.WriteLine(p1.Name); 11 12 // 3.调用两个参数的构造函数创建对象 13 var p2 = new Person("李白", 18); 14 Console.WriteLine("姓名:{0},年龄{1}", p2.Name, p2.Age); 15 Console.ReadKey(); 16 }
下图是上面调用代码的执行结果,结果表明创建3个类实例,static构造函数是由系统自动调用,并且仅仅调用了一次:
第四,静态构造函数的典型用途是:当类使用日志文件时,将使用这种构造函数向日志文件中写入项。静态构造函数在为非托管代码创建包装类时也很有用,此时该构造函数可以调用 LoadLibrary 方法。
第五,如果静态构造函数引发异常,运行时将不会再次调用该构造函数,并且在程序运行所在的应用程序域的生存期内,类型将保持未初始化。
四、私有构造函数
私有构造函数是一种特殊的实例构造函数。它通常用在只包含静态成员的类中。如果类具有一个或多个私有构造函数而没有公共构造函数,则其他类(除嵌套类外)无法创建该类的实例。例如,下面的示例声明了一个名为NLog的类,它只有一个private构造函数和一个静态方法。无法在外部实例化该类,只能访问它的静态成员方法:
1 class MyLog 2 { 3 // 私有构造函数 4 private MyLog() 5 { 6 7 } 8 9 public static void SLog() 10 { 11 Console.WriteLine("我是一个静态成员方法!"); 12 13 } 14 } 15 class Program 16 { 17 static void Main(string[] args) 18 { 19 // 无公开的构造函数,法实例化 20 MyLog myLog = new MyLog(); 21 22 // 只能访问静态成员 23 MyLog.SLog(); 24 25 } 26 }
注意,如果您不对构造函数使用访问修饰符,则在默认情况下它仍为私有构造函数。但是,通常显式地使用 private 修饰符来清楚地表明该类不能被实例化。
五、复制构造函数
如果我们创建了类的一个新的对象并希望从现有对象复制值,那么我们可以声明一个专门用于复制值的构造函数。例如,下面的实例中 Person 类包含一个构造函数,该构造函数接受另一个 Person 类型的对象作为其参数。然后此对象的字段中的内容将分配给新对象中的字段。备用复制构造函数将要复制的对象的 name 和 age 字段发送到类的实例构造函数。
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 // 实例化一个Person类的对象 6 Person person1 = new Person("George", 40); 7 8 // 采用对象person1的值实例化另一个Person类的对象 9 Person person2 = new Person(person1); 10 Console.WriteLine(person2.Details); 11 12 Console.ReadKey(); 13 } 14 } 15 16 class Person 17 { 18 private string name; 19 private int age; 20 21 // 复制构造函数 22 public Person(Person previousPerson) 23 { 24 name = previousPerson.name; 25 age = previousPerson.age; 26 } 27 28 // 实例构造函数 29 public Person(string name, int age) 30 { 31 this.name = name; 32 this.age = age; 33 } 34 35 // Get 访问器 36 public string Details 37 { 38 get 39 { 40 return name + " is " + age.ToString(); 41 } 42 } 43 }
参考:http://msdn.microsoft.com/zh-cn/library/ms173116(VS.100).aspx
六、对象初始化器
一般我们创建对象都是使用对象创建表达式:即“new运算符+类的构造函数”。通过这种方式创建完对象之后,再为对象的属性赋值,很麻烦。有一个对象创建表达式的升级版本,它可以简化对象的创建和初始值设定的过程:就是用对象初始化器。对象初始化器有称为“对象初始值设定项”或“对象初始化列表”,用于在创建对象的表达式中为对象的成员赋初值。下面是使用对象初始化器创建对象的两种方式:
1 // 对象初始化器1:包含参数列表 2 var person1 = new Person("哈哈") { Name = "哈哈" }; 3 4 // 对象初始化器2:省略参数列表(推荐) 5 var person2 = new Person { Name = "汤姆", Age = 18 };
第二种方式推荐使用,因为它更加简洁,参数列表也是没有必要的。此外,推荐使用var关键字代替类型名。
参考:http://msdn.microsoft.com/zh-cn/library/ace5hbzh(VS.100).aspx