C# - 结构体
C# - 结构体(Structs)
Section titled “C# - 结构体(Structs)”在 C# 中,结构体(struct)是一种值类型,用于封装少量相关变量。它们非常适合表示以数据为中心、轻量级的对象,且不需要像继承那样的复杂行为。
可以将结构体视为创建单个变量的蓝图,该变量包含相关的数据点。例如,要表示 RGB 颜色,你可能需要红色(Red)、绿色(Green)和蓝色(Blue)值。结构体可以将这些值组合在一起:
public readonly struct RgbColor{ public byte Red { get; } public byte Green { get; } public byte Blue { get; }
public RgbColor(byte r, byte g, byte b) { Red = r; Green = g; Blue = b; }}关键区别: 最重要的区别在于结构体是值类型,而类是引用类型。当你将结构体传递给方法时,会传递结构体的副本。当你传递类实例时,会传递对该实例的引用。
定义和初始化结构体
Section titled “定义和初始化结构体”你可以使用 struct 关键字定义结构体。现代 C# 的最佳实践鼓励使用 readonly 关键字以及 init 或仅 get 属性使结构体不可变。这可以防止它们在创建后被更改状态,从而带来更可预测的代码。
使用主构造函数定义现代结构体(C# 12+)
Section titled “使用主构造函数定义现代结构体(C# 12+)”C# 12 引入了主构造函数,以实现更简洁的语法。
// 推荐的现代方法(C# 12+)public readonly struct Point(double x, double y){ public double X { get; } = x; public double Y { get; } = y;
// 结构体中的方法 public double DistanceFromOrigin() => Math.Sqrt(X * X + Y * Y);
// 覆写 ToString 方法以更好地显示 public override string ToString() => $"({X}, {Y})";}初始化结构体
Section titled “初始化结构体”你可以使用 new 关键字初始化结构体,这会调用其构造函数。
// 使用上面 Point 结构体的主构造函数var p1 = new Point(3.0, 4.0);var p2 = new Point(-1.0, 5.5);
Console.WriteLine($"Point 1: {p1}");Console.WriteLine($"Point 1 Distance: {p1.DistanceFromOrigin():F2}"); // F2 格式化为两位小数
// 输出:// Point 1: (3, 4)// Point 1 Distance: 5.00注意: 与类不同,你可以在不使用 new 的情况下实例化结构体,但在使用它之前必须初始化其所有字段/属性。使用 new 通常更安全,因为它确保所有成员都初始化为它们的默认值。
类与结构体:何时使用?
Section titled “类与结构体:何时使用?”在类和结构体之间做出选择是一个关键的设计决策。当类型满足以下大部分条件时,请使用结构体:
- 它逻辑上表示一个单一的值,例如数字、日期或点。
- 它占用内存较小(通常小于 16 字节)。
- 它是不可变的。一旦创建,其值不应改变。
- 它不需要频繁地装箱或拆箱(装箱是将值类型转换为引用类型,这可能会影响性能)。
对于其他所有情况,请使用类,特别是对于需要继承、具有唯一标识或其状态会随时间变化的更大、更复杂的对象。
实际应用:一个小型项目
Section titled “实际应用:一个小型项目”让我们创建一个小程序来管理图书记录。我们将定义一个 Book 结构体并在列表中使用它。
using System;using System.Collections.Generic;
// 定义一个不可变的 Book 结构体public readonly struct Book{ public string Title { get; init; } public string Author { get; init; } public int PublicationYear { get; init; }
// 覆写 ToString 方法以获得整洁、可读的输出 public override string ToString() => $"'{Title}' by {Author} ({PublicationYear})";}
// 主应用程序逻辑(使用顶级语句)var library = new List<Book>{ new() { Title = "1984", Author = "George Orwell", PublicationYear = 1949 }, new() { Title = "Dune", Author = "Frank Herbert", PublicationYear = 1965 }, new() { Title = "The Hitchhiker's Guide to the Galaxy", Author = "Douglas Adams", PublicationYear = 1979 }};
Console.WriteLine("Welcome to the Library!");Console.WriteLine("-----------------------");
foreach (var book in library){ Console.WriteLine(book);}
// 如何测试这个逻辑?// 你可以编写一个单元测试来验证创建 Book 并调用 ToString()// 能产生预期的字符串格式。当上述代码编译并执行时,会产生以下结果:
Welcome to the Library!-----------------------'1984' by George Orwell (1949)'Dune' by Frank Herbert (1965)'The Hitchhiker's Guide to the Galaxy' by Douglas Adams (1979)结构体与接口
Section titled “结构体与接口”结构体与类一样,可以实现接口。这对于在不同类型之间提供通用功能很有用。
using System;
// 可表示为字符串的对象的接口public interface IFormattable{ string ToFormattedString();}
// 实现接口的结构体public readonly struct Person(string firstName, string lastName) : IFormattable{ public string FirstName { get; } = firstName; public string LastName { get; } = lastName;
// 使用表达式主体实现接口方法 public string ToFormattedString() => $"Name: {LastName}, {FirstName}";}
// --- 主程序 ---var person = new Person("John", "Doe");Console.WriteLine(person.ToFormattedString());
// 输出:// Name: Doe, John