ZetCode

C# 结构体

最后修改于 2023 年 7 月 5 日

在本文中,我们将使用 C# 中的结构体类型。

结构体类型

结构体是一种值类型。该类型使用 struct 关键字定义。结构体类似于类。结构体旨在表示轻量级对象,例如 PointRectangleColor 等。在许多情况下,结构体可能比类更有效率。结构体是值类型,并在堆栈上创建。请注意,像 intboolfloat 这样的原始数据类型在技术上都是 struct 类型。

所有 struct 类型都继承自 System.ValueType,并进一步继承自 System.Object。结构体永远不会是抽象的,并且总是隐式密封的。因此,结构体类型不支持继承。因此,struct 数据成员不能声明为 protected。抽象和密封修饰符不允许用于 struct 定义。struct 不允许声明无参数构造函数。

结构体还可以包含构造函数、常量、字段、方法、属性、索引器、运算符、事件和嵌套类型。但是,如果我们需要实现更多这些特性,我们可能需要考虑使用类来代替。结构体可以实现接口。struct 可以用作 nullable 类型,并且可以被赋予 null 值。

C# 结构体简单示例

以下示例创建一个简单的结构体。

Program.cs
var p = new Point(2, 5);
Console.WriteLine(p);

public struct Point
{
    private int x;
    private int y;

    public Point(int x, int y)
    {
        this.x = x;
        this.y = y;
    }

    public override string ToString()
    {
        return $"Point x:{x}, y:{y}";
    }
}

该示例创建一个 Point 结构体。该点也可以用类来表示,但是使用 struct 我们可以更有效率;特别是,当我们处理大量点时。

var p = new Point(2, 5);
Console.WriteLine(p);

我们创建 Point 结构体并调用其 ToString 方法。

public struct Point
{
    ...
}

该结构体使用 struct 关键字声明。

public override string ToString()
{
    return $"Point x:{x}, y:{y}";
}

struct 类型不支持继承。但是,我们可以使用 override 关键字来覆盖该 struct 类型隐式继承的方法。 ToString 方法就是这种情况。

$ dotnet run
Point x:2, y:5

没有 new 关键字

可以创建 struct 类型的实例,而无需使用 new 关键字。

Program.cs
Person p;
p.name = "Jane";
p.age = 17;

Console.WriteLine($"{p.name} is {p.age} years old");

public struct Person
{
    public string name;
    public int age;
}

我们有一个 Person 结构体,其中包含两个公共成员。

Person p;

首先,我们声明一个 Person 结构体。

p.name = "Jane";
p.age = 17;

稍后,我们使用一些数据初始化该结构体。

$ dotnet run
Jane is 17 years old

C# 结构体自动实现的属性

我们可以将自动实现的属性与结构体类型一起使用。

Program.cs
var p1 = new Person("John Doe", "gardener");
Console.WriteLine(p1);

var p2 = new Person("Roger Roe", "driver");
Console.WriteLine(p2);

public struct Person
{
    public Person(string name, string occupation)
    {
        this.Name = name;
        this.Occupation = occupation;
    }

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

    public override string ToString()
    {
        return $"{Name} is a(n) {Occupation}";
    }
}

在该程序中,我们有一个 Person 结构体类型,它具有自动实现的 NameOccupation 属性。

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

getset 关键字用于创建自动实现的属性。我们有更简洁和可读的代码。

C# 只读结构体

使用 readonly 修饰符,我们可以创建不可变的结构体类型。只读结构体的所有成员都必须是只读的。字段成员也使用 readonly 修饰符,而自动属性也可以使用 init 关键字。

Program.cs
var p = new Point(3, 7);
Console.WriteLine(p);

public readonly struct Point
{
    public Point(double x, double y)
    {
        X = x;
        Y = y;
    }

    public double X { get; init; }
    public double Y { get; init; }

    public override string ToString()
    {
        return $"({X}, {Y})";
    }
}

在该示例中,我们创建一个点。 Point 结构体具有两个只读属性:XY

public double X { get; init; }
public double Y { get; init; }

init 关键字创建一个 init-only setter。它仅在对象构造期间为属性分配一个值。这强制执行不变性。

C# 结构体是值类型

结构体类型是值类型。它们在堆栈上创建。创建值类型时,仅分配一个内存空间来存储该值。值类型的赋值会复制该值。

Program.cs
var p1 = new Person("Beky", 18);
var p2 = p1;

Console.WriteLine(p2);
p2.Name = "Jane";
p2.Age = 17;

Console.WriteLine(p2);
Console.WriteLine(p1);

public struct Person
{
    public Person(string name, int age) : this()
    {
        this.Name = name;
        this.Age = age;
    }

    public string Name { get; set; }
    public int Age { get; set; }

    public override string ToString()
    {
        return $"{Name} is {Age} years old";
    }
}

我们有一个 Person 结构体,其中包含两个数据成员。我们有一个双参数构造函数,并且我们还使用自动属性。

var p1 = new Person("Beky", 18);
var p2 = p1;

在这里,我们创建一个 struct。然后,将创建的 struct 分配给另一个 struct。我们创建了该结构体的副本。

p2.Name = "Jane";
p2.Age = 17;

我们更改第二个结构体的数据。第一个结构体不受影响,因为我们处理的是原始 struct 类型的副本。

public string Name { get; set; }
public int Age { get; set; }

自动属性可以在 struct 类型中使用。

$ dotnet run
Beky is 18 years old
Jane is 17 years old
Beky is 18 years old

原始类型是结构体

intfloatbool 这样的原始数据类型在底层都是结构体。这与 C++ 或 Java 等语言不同。

Program.cs
float x = 12.3f;
int y = 34;
bool z = false;

Console.WriteLine(x.GetType());
Console.WriteLine(y.GetType());
Console.WriteLine(z.GetType());

我们有三个变量:一个 float、一个 int 和一个 bool。我们对它们中的每一个都调用 GetType 方法。

Console.WriteLine(x.GetType());

我们对 float 值调用 GetType 方法。每个结构体都隐式地继承自包含 GetType 方法的 System.ValueType 类。

$ dotnet run
System.Single
System.Int32
System.Boolean

来源

结构体类型 - 语言参考

在本文中,我们介绍了 C# 中的 struct 类型。

作者

我叫 Jan Bodnar,是一位充满激情的程序员,拥有丰富的编程经验。 我从 2007 年开始撰写编程文章。 迄今为止,我已经撰写了超过 1,400 篇文章和 8 本电子书。 我拥有超过十年的编程教学经验。

列出所有 C# 教程