C# - 构造函数

创建时间:
2014-05-03 12:19
最近更新:
2018-09-30 11:36

Resource - MSDN

  1. 构造函数和析构函数
  2. 构造函数 (C# 编程指南)
  3. 类构造函数
  4. 结构构造函数
  5. 构造函数设计
  6. 类型构造函数设计
  7. 静态构造函数 (C# 编程指南)
  8. 实例构造函数 (C# 编程指南)
  9. 私有构造函数 (C# 编程指南)
  10. Lazy<T> 构造函数 (Boolean)
  11. 使用构造函数 (C# 编程指南)

构造函数

  • 所有类都至少会有 1 个构造函数。
  • 如果没有显式定义任何构造函数,编译器会自动生成 1 个默认构造函数。
  • 如果已经显式定义了 1 个或多个构造函数,编译器就不会再自动生成任何构造函数。
  • 默认构造函数没有参数。
  • 带参数的构造函数称为非默认的构造函数。一个类可以定义 0 至多个非默认的构造函数。

上述行为与 Java 一致。

在 C#中,构造函数用 new 关键字来调用。

注意,如果不对构造函数使用访问修饰符,则在默认情况下它仍为私有构造函数。但是,通常显式地使用 private 修饰符来清楚地表明该类不能被实例化。

如需禁止实例化基类、只允许实例化派生类,那么可以将基类的构造函数/析构函数声明为 protected,而将派生类的构造函数/析构函数声明为 public。2013-09-01 Tony 使用此特性设计了 T.Account 的 Login 基类与 LoginOfUser / Staff 派生类。

private 构造函数 与 空构造函数

将类设计为没有公共的构造函数,可禁止在类的外部实例化它们。
将默认的构造函数设置为 private,可限制外部代码只能使用非默认的构造函数。

private 构造函数示例一

class Program
{
    static void Main(string[] args)
    {
        new B();
    }
}
class A
{
    private A(){ }
}
class B : A
{
    public B() : base() { } //base() 编译时将报错:"A.A()" 不可访问,因为它受保护级别限制。
}

private 构造函数示例二

class Program
{
    static void Main(string[] args)
    {
        new B(3);
    }
}
class A
{
    private int _Int;
    private A(int i)
    {
        _Int = i;
    }
}
class B : A
{
    public B(int i) : base(i) { } //base(i) 编译时将报错:"A.A(int)" 不可访问,因为它受保护级别限制。
}

private 空构造函数示例 (单例)

声明 空构造函数 可阻止 自动生成默认构造函数。
通常在单例模式中使用 private Class() {} 这样的空构造函数 来改变默认构造函数的访问性 以禁止实例化,同时在 属性 或 方法 中创建并返回类的实例,并且此实例存储在该类的一个静态字段中。见以下单例模式示例代码:

//无需 lock 实现单例,但无法精确控制创建实例的时刻:
public sealed class Singleton
{
    private static readonly Singleton _instance = new Singleton();
    private Singleton() { }
    public static Singleton Instance
    {
        get { return _instance; }
    }
}
//错误的实现,无法确保单例:
public sealed class Singleton
{
    private static readonly Singleton _instance;
    private Singleton() { }
    public static Singleton GetInstance()
    {
        return _instance ?? new Singleton();
    }
}

System.Web.Mvc.EmptyResult 类 与 System.Linq.EmptyEnumerable<TElement> 类 均采用此方式实现了单例。详见其源码。

类层次结构中构造函数的执行次序

执行派生类的构造函数时,如果已通过 base 指定基类的构造函数则执行之、如未指定则执行基类的默认构造函数,如此一直执行到 System.Object 的构造函数,因为 System.Object 没有基类,那么它的构造函数中的代码将被直接执行,之后再沿基类返回至派生类的方向,依次执行每个层次的构造函数中的代码,直至需要实例化的类为止。在这个过程中,每个构造函数都初始化它自己的类中的字段。总之,父类构造函数中的代码总是先于子类构造函数中的代码执行。这使得派生类的构造函数可以在执行过程中调用它可以访问的基类的成员,因为基类已经构造完毕,其字段也已经初始化完毕。

在派生类的构造函数中,可以使用 base 关键字来调用基类的构造函数

class A{
    public A(int id) {}
}
class B : A{
    public B() {}
    public B(int id) : base(id) : this() {}
}

basethis 关键字是调用另一个构造函数时允许使用的唯二关键字,其它关键字都会导致编译错误。

在派生类的构造函数中,没有指定、或使用 :base(),都将调用基类的默认构造函数 (无参)。

如果不在派生类构造函数中显式的调用一个基类构造函数,编译器会自动在派生类构造函数名称后面添加 :base() 来调用基类的默认构造函数。与此类似,Java 编译器则会在子类的构造函数中第一行代码前面加上一句 super()