65.9K
CodeProject 正在变化。 阅读更多。
Home

C# 构造函数入门

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.94/5 (138投票s)

2004 年 5 月 11 日

7分钟阅读

viewsIcon

789798

这是一篇关于 C# 构造函数的文章,面向初学者。它涵盖了简单的构造函数、构造函数重载、继承中构造函数的行为、构造函数链以及静态构造函数。最后,它包含了关于构造函数的常见 FAQ。

引言

广义上讲,构造函数是类中在其对象创建时执行的方法。通常,我们将初始化代码放在构造函数中。在类中编写构造函数非常简单,看下面的示例

public class mySampleClass
{
    public mySampleClass()
    {
        // This is the constructor method.
    }
    // rest of the class members goes here.
}

当此类的对象被实例化时,将执行此构造函数。类似这样

mySampleClass obj = new mySampleClass()
// At this time the code in the constructor will // be executed

构造函数重载

C# 支持构造函数的重载,这意味着我们可以 having 具有不同参数集的构造函数。因此,我们的类可以如下所示

public class mySampleClass
{
    public mySampleClass()
    {
        // This is the no parameter constructor method.
        // First Constructor
    }

    public mySampleClass(int Age)
    {
        // This is the constructor with one parameter.
        // Second Constructor
 
    }

    public mySampleClass(int Age, string Name)
    {
        // This is the constructor with two parameters.
        // Third Constructor
    }

    // rest of the class members goes here.
}

好的,请注意,对构造函数的调用现在取决于您实例化对象的方式。例如

mySampleClass obj = new mySampleClass()
// At this time the code of no parameter  
// constructor (First Constructor)will be executed
 
mySampleClass obj = new mySampleClass(12)
// At this time the code of one parameter  
// constructor(Second Constructor)will be 
// executed.

对构造函数的调用完全受重载规则的约束。

从另一个构造函数调用构造函数

您始终可以在一个构造函数内部调用另一个构造函数。例如

public class mySampleClass
{
    public mySampleClass(): this(10)
    {
        // This is the no parameter constructor method.
        // First Constructor
    }

    public mySampleClass(int Age) 
    {
        // This is the constructor with one parameter.
        // Second Constructor
    }
}

首先,让我们看看这个语法是什么

public mySampleClass(): this(10)

在这里,this 指的是同一个类,所以当我们说 this(10) 时,我们实际上是指执行 public mySampleClass(int Age) 方法。上面调用方法的方式称为初始化程序。我们最多可以在方法中使用一个这样的初始化程序。

我们还必须了解执行顺序,即哪个方法何时执行。在这里,如果我这样实例化对象

mySampleClass obj = new mySampleClass()

那么 public mySampleClass(int Age) 的代码将在 mySampleClass() 的代码之前执行。所以,实际上该方法的定义

public mySampleClass(): this(10)
{
    // This is the no parameter constructor method.
    // First Constructor
}

等同于

public mySampleClass()
{
    mySampleClass(10) 
    // This is the no parameter constructor method.
    // First Constructor
}

注意:上面的代码(就在这行代码的上方)仅为类比提及,不会编译。目的是说明如果使用初始化程序,执行流程。

我们不能像处理简单方法一样显式调用 C# 中的构造函数,例如:上面代码中的语句 mySampleClass(10) 将不起作用。您只能通过初始化程序从一个构造函数调用另一个构造函数。

对于 VB.NET 程序员:您可以通过语法 Me.New(param list) 调用同一类的另一个构造函数,但它应该是调用构造函数方法的第一行。因此,最终,被调用构造函数中的代码将在调用构造函数中的代码之前运行,这在这里与初始化程序相同。

请注意,在初始化程序中只允许使用 thisbase(稍后会介绍)关键字,其他方法调用将引发错误。

这有时被称为构造函数链。

呼……简单的事情变得困难,但就是这样。无论如何,让我们继续。

继承中构造函数的行为

首先,让我们创建派生类。

public class myBaseClass
{
    public myBaseClass()
    {
        // Code for First Base class Constructor
    }

    public myBaseClass(int Age)
    {
        // Code for Second Base class Constructor
    }

    // Other class members goes here
}

public class myDerivedClass : myBaseClass
// Note that I am inheriting the class here.
{
    public myDerivedClass()
    {
        // Code for the First myDerivedClass Constructor.
    }

    public myDerivedClass(int Age):base(Age)
    {
        // Code for the Second myDerivedClass Constructor.
    }

    // Other class members goes here
}

现在,这里的执行顺序是什么

如果我这样创建派生类的对象

myDerivedClass obj = new myDerivedClass()

那么执行顺序将是

  1. public myBaseClass() 方法。
  2. 然后是 public myDerivedClass() 方法。

注意:如果我们没有提供引用基类构造函数的初始化程序,它将执行基类的无参数构造函数。

请注意一点:我们既没有通过初始化程序也没有通过 base 关键字显式调用基类的构造函数,但它仍然在执行。这是构造函数的正常行为。

如果我这样创建派生类的对象

myDerivedClass obj = new myDerivedClass(15)

那么执行顺序将是

  1. public myBaseClass(int Age) 方法
  2. 然后是 public myDerivedClass(int Age) 方法

在这里,新的关键字 base 出现了。它引用当前类的基类。所以,这里它指的是 myBaseClass。而 base(10) 指的是调用 myBaseClass(int Age) 方法。

另请注意 Age 变量在语法中的用法:public myDerivedClass(int Age):base(Age)。 [理解它留给读者]。

私有构造函数

私有构造函数,即带有“private”访问修饰符的构造函数,是一个特殊的案例。因为我们既不能创建类的对象,也不能仅通过 private 构造函数来继承类。但是,是的,我们可以在类中拥有 public 构造函数集以及 private 构造函数,并且 public 构造函数可以通过构造函数链从类内部访问 private 构造函数。

例如,我的类是这样的

public class myClass
{
    private MyClass()
    {
        Console.WriteLine("This is no parameter Constructor");
    }

    public MyClass(int var):this()
    {
        Console.WriteLine("This is one parameter Constructor");
    }    
    // Other class methods goes here
}

然后我们可以通过以下语句创建这个类的对象

MyClass obj = new MyClass(10);

上面的语句将正常工作,但是下面的语句

MyClass obj = new MyClass();

将引发错误:'Constructors.MyClass.MyClass()' is inaccessible due to its protection level

可以拥有只有私有构造函数的类。但是是的,如我所说,这样的类既不能实例化也不能继承。如果我们尝试继承一个只有私有构造函数的类,我们将得到与上面相同的错误。还请记住,一旦您自己提供了构造函数,编译器就不会为您的类添加无参数的公共构造函数。

好吧,这类类的一个使用场景可能是——当类中只有静态成员而不需要实例化它时。

呼……迷失了……构造函数还有什么没讲的吗?是的,静态构造函数。哈!!那么,它们是什么?让我们看看……

静态构造函数

这是 C# 中引入的新概念。所谓新,我是指它在 C++ 程序员中不可用。这是一个特殊的构造函数,在类的第一个对象创建之前被调用。执行时间无法确定,但肯定是在第一个对象创建之前——可能是在加载程序集的时。

编写静态构造函数的语法也非常简单。这是

public class myClass
{
    static myClass()
    {
        // Initialization code goes here.
        // Can only access static members here.
    }
    // Other class methods goes here
}

静态构造函数的注意事项

  1. 类中只能有一个静态构造函数。
  2. 静态构造函数应不带参数。
  3. 它只能访问类的静态成员。
  4. 静态构造函数定义中不应包含访问修饰符。

好的,以上所有几点都还可以,但为什么会这样呢?让我们一步一步来。

首先,静态方法的调用是由 CLR 制作的,而不是由对象制作的,所以我们不需要为它设置访问修饰符。

其次,它将由 CLR 调用,CLR 可以根据需要向其传递参数。所以我们不能有带参数的静态构造函数。

第三,类中的非静态成员是实例特定的。所以静态构造函数,如果允许操作非静态成员,将反映在所有实例对象中,这是不切实际的。所以静态构造函数只能访问类的静态成员。

第四,重载需要两个方法在方法定义方面有所不同,而这对于静态构造函数来说是做不到的,所以你最多只能有一个静态构造函数。

现在,这里有一个问题,我们可以有两个构造函数,比如

public class myClass
{
    static myClass()
    {
        // Initialization code goes here.
        // Can only access static members here.
    }
    public myClass()
    {
        // Code for the First myDerivedClass Constructor.
    }

    // Other class methods goes here
}

这是完全有效的,尽管似乎不符合重载概念。但为什么?因为两个方法的执行时间是不同的。一个是程序集加载时,一个是对象创建时。

构造函数 FAQ

  1. 类必须有构造函数吗?

    是的,类必须有构造函数,而且该构造函数必须对对象可访问,即它应该具有正确的访问修饰符。例如,如果我们类中只有私有构造函数,而我们有兴趣实例化该类,即想创建类的对象,那么只有私有构造函数是不够的,事实上它会引发错误。所以,应该为构造函数提供正确的访问修改符。

  2. 如果我不写构造函数怎么办?

    在这种情况下,编译器将在后台尝试为您的类提供无参数构造函数。编译器只有在您没有为类编写构造函数时才会尝试此操作。如果您提供任何构造函数(带或不带参数),编译器将不会进行此类尝试。

  3. 如果我有一个构造函数 public myDerivedClass(),但没有 public myBaseClass() 怎么办?

    这将引发错误。如果无参数构造函数缺失或不可访问(例如它是 private),它将引发错误。您需要在此处采取预防措施。

  4. 我们可以从非静态(普通)构造函数访问静态成员吗?

    是的,我们可以。对非静态构造函数没有这样的限制。但是静态构造函数有一个限制,即它只能访问静态成员。

© . All rights reserved.