声明实例构造函数以指定在使用 new
表达式创建类型的新实例时执行的代码。 若要初始化非静态类中的 静态 类或静态变量,可以定义 静态构造函数。
如以下示例所示,可以在一种类型中声明多个实例构造函数:
class Coords
{
public Coords()
: this(0, 0)
{ }
public Coords(int x, int y)
{
X = x;
Y = y;
}
public int X { get; set; }
public int Y { get; set; }
public override string ToString() => $"({X},{Y})";
}
class Example
{
static void Main()
{
var p1 = new Coords();
Console.WriteLine($"Coords #1 at {p1}");
// Output: Coords #1 at (0,0)
var p2 = new Coords(5, 3);
Console.WriteLine($"Coords #2 at {p2}");
// Output: Coords #2 at (5,3)
}
}
在前面的示例中,第一个无参数构造函数调用两个参数相等 0
的第二个构造函数。 为此,请使用 this
关键字。
在派生类中声明实例构造函数时,可以调用基类的构造函数。 为此,请使用 base
关键字,如以下示例所示:
abstract class Shape
{
public const double pi = Math.PI;
protected double x, y;
public Shape(double x, double y)
{
this.x = x;
this.y = y;
}
public abstract double Area();
}
class Circle : Shape
{
public Circle(double radius)
: base(radius, 0)
{ }
public override double Area() => pi * x * x;
}
class Cylinder : Circle
{
public Cylinder(double radius, double height)
: base(radius)
{
y = height;
}
public override double Area() => (2 * base.Area()) + (2 * pi * x * y);
}
class Example
{
static void Main()
{
double radius = 2.5;
double height = 3.0;
var ring = new Circle(radius);
Console.WriteLine($"Area of the circle = {ring.Area():F2}");
// Output: Area of the circle = 19.63
var tube = new Cylinder(radius, height);
Console.WriteLine($"Area of the cylinder = {tube.Area():F2}");
// Output: Area of the cylinder = 86.39
}
}
无参数构造函数
如果 类 没有显式实例构造函数,C# 提供了一个可用于实例化该类实例的无参数构造函数,如以下示例所示:
public class Person
{
public int age;
public string name = "unknown";
}
class Example
{
static void Main()
{
var person = new Person();
Console.WriteLine($"Name: {person.name}, Age: {person.age}");
// Output: Name: unknown, Age: 0
}
}
该构造函数根据相应的初始化器来初始化实例字段和属性。 如果字段或属性没有初始值设定项,则其值设置为字段或属性类型的 默认值 。 如果在类中声明了至少一个实例构造函数,C# 不提供无参数构造函数。
结构类型始终提供无参数构造函数。 无参数构造函数是一个隐式无参数构造函数,它生成类型的默认值或显式声明的无参数构造函数。 有关详细信息,请参阅结构类型文章的结构初始化和默认值部分。
主构造函数
从 C# 12 开始,可以在类和结构中声明 主构造函数 。 将任何参数置于类型名称后面的括号中。
public class NamedItem(string name)
{
public string Name => name;
}
主构造函数的参数位于声明类型的整个主体中。 它们可以初始化属性或字段。 它们可以用作方法或本地函数中的变量。 可以将其传递给基本构造函数。
主构造函数指示这些参数对于类型的任何实例都是必需的。 任何显式写入的 this(...)
构造函数都必须使用初始值设定项语法来调用主构造函数。 这确保主构造函数的参数由所有构造函数明确分配。 对于任何 class
类型(包括 record class
类型),当主构造函数存在时,不会发出隐式无参数构造函数。 对于任何 struct
类型(包括 record struct
类型)始终发出隐式无参数构造函数,并将所有字段(包括主构造函数参数)初始化为 0 位模式。 如果编写显式无参数构造函数,则必须调用主构造函数。 在这种情况下,可以为主构造函数参数指定不同的值。 以下代码显示了主要构造函数的示例。
// name isn't captured in Widget.
// width, height, and depth are captured as private fields
public class Widget(string name, int width, int height, int depth) : NamedItem(name)
{
public Widget() : this("N/A", 1,1,1) {} // unnamed unit cube
public int WidthInCM => width;
public int HeightInCM => height;
public int DepthInCM => depth;
public int Volume => width * height * depth;
}
可以通过在属性上指定 method:
目标,将属性添加到合成的主要构造函数方法:
[method: MyAttribute]
public class TaggedWidget(string name)
{
// details elided
}
如果未指定 method
目标,则特性将放置在类而不是方法上。
在 class
类型中和 struct
类型中,主要构造函数参数可在类型正文中的任意位置使用。 可以将参数实现为捕获的专用字段。 如果对参数的唯一引用是初始值设定项和构造函数调用,则不会在专用字段中捕获该参数。 在类型的其他成员中使用会导致编译器捕获专用字段中的参数。
如果类型包含 record
修饰符,编译器将合成与主构造函数参数同名的公共属性。 对于 record class
类型,如果主构造函数参数使用与基本主构造函数相同的名称,该属性是基 record class
类型的公共属性。 它在派生的 record class
类型中不会重复。 这些属性不会为非record
类型生成。