ZetCode

C# 构造函数

最后修改于 2023 年 7 月 5 日

C# 构造函数教程展示了如何在 C# 语言中使用构造函数。

构造函数是在创建对象时调用的方法。类、结构和记录都有构造函数。构造函数的目的是初始化对象的状态。

构造函数不返回值,也不使用 void 关键字。它与类具有相同的名称。构造函数可以被重载;即,可以有多个构造函数(具有相同的名称),但具有不同的参数。

构造函数不能被继承。它们按照继承的顺序被调用。如果我们没有为类编写任何构造函数,C# 会提供一个隐式的默认构造函数。如果我们提供了任何类型的构造函数,则不会提供默认构造函数。

构造函数不能是抽象的、final 的和同步的。

C# 构造函数示例

构造函数用于初始化字段。

Program.cs
var name = "Lenka";
var dob = new DateTime(1990, 3, 5);

var u = new User(name, dob);
Console.WriteLine(u);

class User
{
    private DateTime Born;
    private string Name;

    public User(string Name, DateTime Born)
    {
        this.Name = Name;
        this.Born = Born;
    }

    public override string ToString() =>
        $"{this.Name} was born on {this.Born.ToShortDateString()}";
}

我们有一个 User 类的构造函数。

var u = new User(name, dob);

我们创建 User 对象,并将其构造函数的两个值传递给它。这是调用对象构造函数的时间。

public User(string Name, DateTime Born)
{
    this.Name = Name;
    this.Born = Born;
}

在构造函数方法中,我们初始化两个属性:NameBorn。由于属性和构造函数参数的名称相同,因此必须使用 this 关键字。它用于引用类的属性。

$ dotnet run
Lenka was born on 3/5/1990

如果我们为构造函数参数使用不同的名称,则不需要 this 关键字。

Program.cs
var name = "Lenka";
var dob = new DateTime(1990, 3, 5);

var u = new User(name, dob);
Console.WriteLine(u);

class User
{
    private DateTime Born;
    private string Name;

    public User(string _Name, DateTime _Born)
    {
        Name = _Name;
        Born = _Born;
    }

    public override string ToString() =>
        $"{this.Name} was born on {this.Born.ToShortDateString()}";
}

由于我们使用了 _Name_Born 构造函数参数名称,它们与 NameBorn 属性不同,我们可以省略 this 关键字。

C# 默认构造函数

默认构造函数是不带任何参数的构造函数。

如果我们不提供任何构造函数,C# 默认会创建一个构造函数,它会实例化对象并将成员变量设置为默认值。

Program.cs
var u = new User();

Console.WriteLine(string.IsNullOrEmpty(u.Name));
Console.WriteLine(string.IsNullOrEmpty(u.Occupation));
Console.WriteLine(string.IsNullOrEmpty(u.Dob.ToString()));

Console.WriteLine(u);

class User
{
    public string Name { get; set; }
    public string Occupation { get; set; }
    public DateTime Dob { get; set; }

    public override string ToString() =>
        $"User {{ {this.Name} {this.Occupation} {this.Dob} }}";
}

我们有一个 User 类;我们没有提供自己的构造函数,因此 C# 创建了一个默认构造函数。 string 数据类型的默认值是空字符串,DateTime 的默认值是 DateTime.MinValue

$ dotnet run
True
True
False
User {   1/1/0001 12:00:00 AM }

C# 重载构造函数

构造函数可以被重载。

Program.cs
var u1 = new User();
var u2 = new User("Tom");

class User
{
    public User()
    {
        Console.WriteLine("User is created");
    }

    public User(string name)
    {
        Console.WriteLine($"User {name} is created");
    }
}

User 类中,我们有两个构造函数。 C# 会根据我们传递给它们的参数来调用这些构造函数。

如果我们提供自定义构造函数,C# 不会创建默认构造函数。我们可以显式地创建一个默认的无参数构造函数。

var u1 = new User();

这里,调用了默认的无参数构造函数。

var u2 = new User("Tom");

这里,调用了带有单个参数的第二个构造函数。

$ dotnet run
User is created
User Tom is created

C# 表达式体构造函数

我们可以创建表达式体构造函数。对于较短的构造函数,它们提供了更简洁且外观更好的语法。

Program.cs
var u1 = new User("John Doe", "gardener");
var u2 = new User("Roger Roe", "driver");

Console.WriteLine(u1);
Console.WriteLine(u2);

class User
{
    private string Name;
    private string Occupation;

    public User(string Name, string Occupation) =>
        (this.Name, this.Occupation) = (Name, Occupation);

    public override string ToString() =>
        $"User {{ {this.Name} {this.Occupation} }}";
}

我们有一个带有两个参数的构造函数;它们在表达式体中设置。

public User(string Name, string Occupation) =>
    (this.Name, this.Occupation) = (Name, Occupation);

在这种情况下,this 关键字是强制性的。

C# 构造函数链

构造函数链是一个类从一个构造函数调用另一个构造函数的能力。要从同一个类调用另一个构造函数,我们使用 this 关键字。

Program.cs
var c1 = new Circle(5);
var c2 = new Circle();

class Circle
{
    public Circle(int radius)
    {
        Console.WriteLine($"Circle, r={radius} is created");
    }

    public Circle() : this(1) { }
}

我们有一个 Circle 类。该类有两个构造函数:第一个带有单个参数,第二个不带任何参数。

public Circle(int radius)
{
    Console.WriteLine("Circle, r={0} is created", radius);
}

此构造函数接受一个参数 - radius

public Circle() : this(1) { }

这是不带参数的构造函数。它只是调用另一个构造函数并为其提供默认半径 1。

$ dotnet run
Circle, r=5 is created
Circle, r=1 is created

C# 基类构造函数

当实例化派生类时,会自动调用基类构造函数。

Program.cs
var u1 = new User();

Console.WriteLine("---------------");

var u2 = new User("Tom");

class Base
{
    public Base()
    {
        Console.WriteLine("Base constructor called");
    }
}

class User : Base
{
    public User()
    {
        Console.WriteLine("User constructor called");
    }

    public User(string user)
    {
        Console.WriteLine($"User {user} created");
    }
}

User 类继承自 Base 类。两个构造函数都调用基类构造函数。

$ dotnet run
Base constructor called
User constructor called
---------------
Base constructor called
User Tom created

使用 base 关键字,我们可以指定在创建派生类实例时应调用哪个基类构造函数。

Program.cs
var u1 = new User();

Console.WriteLine("---------------");

var u2 = new User("Tom");

class Base
{
    public Base()
    {
        Console.WriteLine("Base() called");
    }

    public Base(string name)
    {
        Console.WriteLine("Base(string name) called");
    }
}

class User : Base
{
    public User()
    {
        Console.WriteLine("User created");
    }

    public User(string name) : base(name) 
    {
        Console.WriteLine($"User {name} created");
    }
}

现在 Base 类有两个构造函数。我们可以使用 base 关键字指定要调用的构造函数。

public User(string name) : base(name) 
{
    Console.WriteLine($"User {name} created");
}

我们告诉构造函数调用带有单个参数的基类构造函数。

$ dotnet run
Base() called
User created
---------------
Base(string name) called 
User Tom created

C# 复制构造函数

通过从另一个对象复制变量来创建对象的构造函数称为复制构造函数。

Program.cs
var u1 = new User("John Doe", "gardener");
var u2 = new User(u1);
u2.Name = "Roger Roe";

Console.WriteLine(u1);
Console.WriteLine(u2);

class User
{
    public string Name { get; set; }
    public string Occupation { get; set; }

    public User(string Name, string Occupation) =>
        (this.Name, this.Occupation) = (Name, Occupation);

    public User(User user)
    {
        this.Name = user.Name;
        this.Occupation = user.Occupation;
    }

    public override string ToString() =>
        $"User {{ {this.Name} {this.Occupation} }}";
}

User 类包含一个复制构造函数。

public User(User user)
{
    this.Name = user.Name;
    this.Occupation = user.Occupation;
}

类的属性从作为参数传递给构造函数的对象中赋值。

$ dotnet run
User { John Doe gardener }
User { Roger Roe gardener }

C# 私有构造函数

其他类无法创建仅具有私有构造函数的类的实例。私有构造函数通常用于仅具有静态成员的类中。

Program.cs
Console.WriteLine(MyMath.Pow(2));
Console.WriteLine(MyMath.Pow(2, 3));
Console.WriteLine(MyMath.Pow(2, 5));

// var mm = new MyMath();
// Console.WriteLine(mm.GetType());

class MyMath
{
    private MyMath() { }

    public static int Pow(int x, int y = 2)
    {
        int val = 1;

        for (int i = 0; i < y; i++)
        {
            val *= x;
        }

        return val;
    }
}

我们有一个 MyMath 类,它包含一个唯一的静态成员。它的设计目的是在不创建其实例的情况下使用 - 私有构造函数禁止这样做。

$ dotnet run
4
8
32

C# 静态构造函数

静态构造函数用于初始化静态数据或执行只需要执行一次的操作。它在创建第一个实例或引用任何静态成员之前自动调用。

Program.cs
var runner1 = new Runner(1);

Thread.Sleep(700);

var runner2 = new Runner(2);

Thread.Sleep(1100);

Console.WriteLine(runner1.Finish());
Console.WriteLine(runner2.Finish());

class Runner
{
    private static readonly DateTime StartTime;
    private long Id;

    static Runner() => StartTime = DateTime.Now;

    public Runner(long Id) => this.Id = Id;

    public string Finish()
    {
        DateTime EndTime = DateTime.Now;
        return $"Runner {this.Id} finished in {EndTime - StartTime}";
    }
}

我们有一个静态只读变量,名为 StartTime。当第一个 Runner 被创建时,它使用静态构造函数进行初始化。

static Runner() => StartTime = DateTime.Now;

一旦初始化,静态 StartTime 可用于 Runner 的所有实例。

$ dotnet run
Runner 1 finished in 00:00:01.8267889
Runner 2 finished in 00:00:01.8900243

来源

构造函数

在本文中,我们使用了 C# 中的构造函数。

作者

我叫 Jan Bodnar,是一位热情的程序员,拥有丰富的编程经验。 自 2007 年以来,我一直撰写编程文章。 迄今为止,我已经撰写了超过 1,400 篇文章和 8 本电子书。 我在编程教学方面拥有超过十年的经验。

列出所有 C# 教程