首页 > 编程笔记 > C#笔记 阅读:25

C#构造函数和析构函数的用法(附带实例)

C# 程序中,构造函数执行类或结构体的初始化代码。

构造函数的定义和方法的定义类似,区别仅在于构造函数名和返回值只能与封装它的类型相同:
Panda p = new Panda("Petey");  // 调用构造函数

public class Panda
{
    string name;                // 定义字段
    public Panda(string n)      // 定义构造函数
    {
        name = n;               // 初始化代码(设置字段)
    }
}
实例构造函数支持以下的修饰符:
仅包含一个语句的构造函数也可以使用表达式体成员的写法,例如:
using System;

public class Wine
{
    public decimal Price;
    public int Year;
    public Wine(decimal price) { Price = price; }
    public Wine(decimal price, int year) : this(price) { Year = year; }
}

类或者结构体可以重载构造函数。为了避免重复代码,构造函数可以用 this 关键字调用另一个构造函数:
using System;

public class Wine
{
    public decimal Price;
    public int Year;

    public Wine(decimal price) { Price = price; }
    public Wine(decimal price, int year) : this(price, year.Year) { }
}
当构造函数调用另一个构造函数时,被调用的构造函数先执行。

还可以向另一个构造函数传递表达式:
public Wine(decimal price, DateTime year) : this(price, year.Year) { }
表达式内不能使用 this 引用,例如,不能调用实例方法(这是强制性的,由于这个对象当前还没有通过构造函数初始化完毕,因此调用任何方法都有可能失败)。但是表达式可以调用静态方法。

C# 编译器会自动为没有显式定义构造函数的类生成无参数公有构造函数。但是,一旦显式定义了至少一个构造函数,系统就不再自动生成无参数的构造函数了。

我们知道,字段可以在声明时初始化为其默认值:
class Player
{
    int shields = 50;    // Initialized first
    int health = 100;    // Initialized second
}
字段的初始化按声明的先后顺序,在构造函数之前执行。

构造函数不一定都是公有的。通常,定义非公有的构造函数是为了通过一个静态方法调用来控制创建类实例的过程。静态方法可以从一个池中返回对象,而不必每次创建一个新对象的实例。静态方法还可以根据不同的输入参数返回不同的子类对象:
public class Class1
{
    Class1() {} // Private constructor

    public static Class1 Create(...)
    {
        // Perform custom logic here to return an instance of Class1
        ...
    }
}

C#析构函数

析构函数(也称为解构器、解构方法)就像构造函数的反过程,构造函数使用若干值作为参数,并且将它们赋值给字段,而析构函数则相反,将字段反向赋值给若干变量。

析构函数的名字必须为 Deconstruct,并且拥有一个或多个 out 参数,例如:
class Rectangle
{
    public readonly float Width, Height;

    public Rectangle(float width, float height)
    {
        Width = width;
        Height = height;
    }

    public void Deconstruct(out float width, out float height)
    {
        width = Width;
        height = Height;
    }
}
若要调用析构函数,则需使用如下的特殊语法:
var rect = new Rectangle(3, 4);
(float width, float height) = rect; // Deconstruction
Console.WriteLine(width + " " + height); // 3 4

第二行是解构调用,它创建了两个局部变量并调用 Deconstruct 方法。上述解构调用等价于:
float width, height;
rect.Deconstruct(out width, out height);
或者:
rect.Deconstruct(out var width, out var height);

解构调用允许隐式类型推断,因此我们可以将其简写为:
(var width, var height) = rect;
或者:
var (width, height) = rect;

在解构过程中,如果并非对所有的变量都感兴趣,则可以使用丢弃符号(_)来忽略它们:
var (_, height) = rect;
以上写法比起定义一个不会使用的变量更能够体现出程序的本意。

如果解构中的变量已经定义过了,那么可以忽略类型声明:
float width, height;
(width, height) = rect;

上述操作也称为解构赋值。我们可以通过解构赋值来简化类的构造函数:
public Rectangle(float width, float height) =>
    (Width, Height) = (width, height);

我们还可以通过重载 Deconstruct 方法向调用者提供一系列解构方案。Deconstruct 方法可以是扩展方法,这种做法可方便地对第三方作者的类型进行解构。

在 C# 10 中,我们可以在解构时同时匹配现有变量并声明新变量,例如:
double x1 = 0;
(x1, double y2) = rect;

相关文章