代码改变世界

构造函数

2018-06-04 17:30  bsdbqling  阅读(264)  评论(0)    收藏  举报

 

实例构造函数

 

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、静态变量 > 静态构造函数 > 构造函数

 

C#高效编程改进C#代码的50个行之有效的办法(第2版)里说到这样一段话:

 

类型实例的完整过程。你需要理解这些操作的顺序,以及对象的默认初始化操作。你要保证在构造的过程中对每个成员变量仅初始化一次。实现这一点最好的方法就是,尽可能的早地进行初始化。
下面就是创建某个类型的第一个实例时,所进行的操作顺序为:
(1)静态变量设置为0
(2)执行静态变量初始化器
(3)执行基类的静态构造函数
(4)执行静态构造函数
(5)实例变量设置为0
(6)执行衯变量初始化器
(7)执行基类中合适的实例构造函数
(8)执行实例构造函数 
同样类型的第二个以及以后的实例将从第5步开始执行,因为类的构造器仅会执行一次。此外,第6步和第7步将被优化,以便构造函数初始化器使编译器移除重复的指令。

 

练习题:

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();
    }
}

 

下面公布答案:

 

复制代码
public class A
{
    public static readonly int x;
    static A()
    {
        //第二步,调用B.y,此处B.y = 0,因为int类型在初始化阶段,会给赋默认值,默认值为0。最后x = 0 + 1(返回给第一步)
        x = B.y + 1;
    }
}

public class B
{
    //第一步,调用A.x,然后执行类A的静态构造函数,等待返回(第二步返回的A.x = 1,所有y = 1 + 1)
    public static int y = A.x + 1;
    public static void Main(string[] args)
    {
        //第三步,A.x = 1,y = 2。
        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

私有构造函数

私有构造函数是一种特殊的实例构造函数。 它通常用于只包含静态成员的类中。 如果类具有一个或多个私有构造函数而没有公共构造函数,则其他类(除嵌套类外)无法创建该类的实例。

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();
        }
    }
}

 

初始化器分为对象初始化器和集合初始化器

对象初始化器的作用,简单点说就是可以使我们初始化一个类的代码变得更简洁。比如下面这个类:

class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Address { get; set; }
}

我们要初始化它并对它赋值的话通常要这样:

Person person = new Person();
person.Name = "Slark";
person.Age = 100;
person.Address = "Xi'an";

这里我们用了一行创建对象语句,加三行赋值语句。这里光person这个变量就出现了4遍,繁琐。用对象初始化器来代替这些操作:

Person person = new Person { Name = "Slark", Age = 100, Address = "Xi'an" };

同样的效果,对象初始化器只用了一行,简洁!可以看到对象初始化器将创建对象和赋值合为一行,其中赋值就是在后面大括号里做的。这里的赋值可以给所有属性赋值,也可以给部分属性赋值。

既然要讲集合初始化器,那么我们就先来创建一个集合:

List<int> intList = new List<int>();
intList.Add(1);
intList.Add(2);
intList.Add(3);

好,这里我们用4行代码创建了一个3个元素的集合。集合初始化器的出现大大减少了我们对这种操作的代码量。其对应的集合初始化器代码为:

List<int> intList = new List<int> { 1, 2, 3 };

 

嗯,真的少写了很多。集合初始化器的原理也很简单,就是它默默的为我们调用了List的Add方法依次为我们添加了这3个元素。

这里我们再给一个综合运用集合初始化器和对象初始化器的例子:

List<Person> personList = new List<Person>
{
    new Person { Name = "Slark1", Age = 101, Address = "Xi'an1" },
    new Person { Name = "Slark2", Age = 102, Address = "Xi'an2" },
    new Person { Name = "Slark3", Age = 103, Address = "Xi'an3" }
};

代码里用集合初始化器初始化了personList,然后用对象初始化器初始化了3个Person对象实例。

C#构造函数初始化符可以包含对同一个类的另一个构造函数的调用(使用前面介绍的语法),也可以包含对直接基类的构造函数的调用(使用相同的语法,但应使用base关键字代替this)。初始化符中不能有多个调用。 

class Car {
private string description;
private uint nWheels;
public Car(string description, uint nWheels)
{
    this.description = description;
    this.nWheels = nWheels;
    
    public Car(string description) : this(description, 4)   { }
    // etc