C#基础 [10] 构造函数

一、构造函数概述

   构造函数是在创建给定类型的对象时执行的类方法。构造函数具有与类相同的名称,它是类或结构中第一个被执行的方法,通常用于申请内存、初始化新对象的数据成员。任何时候,只要创建类或结构,就会调用它的构造函数。构造函数可以重载,即类或结构可能有多个接受不同参数的构造函数。构造函数使得程序员可设置默认值、限制实例化以及编写灵活且便于阅读的代码。构造函数也被称为构造器或构造方法。

二、实例构造函数

  1.默认构造函数

     如果在类声明中没有显式声明构造函数,那么编译器会自动生成一个隐式的默认构造函数。该构造函数的函数名和类名相同、public、没有参数、方法体为空,它实例化对象,并且将成员字段初始化为成员类型的默认值。无论何时,只要使用 new 运算符实例化对象,并且不为 new 提供任何参数,就会调用默认构造函数。下面是一个Person类的声明,没有显式声明构造函数。

View Code
 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属性和两个构造函数的重载:

View Code
 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     }

    下面分别调用各个构造函数实例化对象:

View Code
 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构造函数:

View Code
 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类的实例:

View Code
 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构造函数和一个静态方法。无法在外部实例化该类,只能访问它的静态成员方法:

View Code
 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 字段发送到类的实例构造函数。

View Code
 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

posted @ 2013-01-02 12:42  YunshiSun  阅读(836)  评论(2编辑  收藏  举报