一、什么是结构(Struct)?

结构(struct)是 C# 中一种值类型数据结构,用于封装小型相关数据。它与类(class)类似,可包含字段、属性、方法等成员,但本质是值类型(存储在栈上),而类是引用类型(存储在堆上)。

核心特点

  • 值类型,赋值时直接复制数据(而非引用);
  • 适合存储小型、简单的数据(如坐标、颜色、日期等);
  • 不能被继承(密封类型),但可实现接口。
二、结构的定义语法
// 基本语法
访问修饰符 struct 结构名 : 接口1, 接口2...
{
    // 成员(字段、属性、方法、构造函数等)
}

示例:定义一个表示二维坐标的结构

public struct Point
{
    // 字段(通常私有,通过属性暴露)
    private int _x;
    private int _y;

    // 构造函数(必须初始化所有字段)
    public Point(int x, int y)
    {
        _x = x;
        _y = y;
    }

    // 属性(封装字段,提供访问控制)
    public int X 
    { 
        get => _x; 
        set => _x = value; 
    }
    public int Y 
    { 
        get => _y; 
        set => _y = value; 
    }

    // 方法(定义行为)
    public void Move(int deltaX, int deltaY)
    {
        _x += deltaX;
        _y += deltaY;
    }

    // 重写ToString()方法
    public override string ToString()
    {
        return $"({X}, {Y})";
    }
}
三、结构的核心特性
1. 是值类型,存储在栈上
  • 结构的实例直接存储数据(而非引用),赋值时会复制完整数据。

示例:

Point p1 = new Point(1, 2);
Point p2 = p1; // 复制p1的所有数据到p2(值类型特性)
p2.X = 100;    // 修改p2不会影响p1

Console.WriteLine(p1); // 输出:(1, 2)
Console.WriteLine(p2); // 输出:(100, 2)
2. 不能有无参数构造函数
  • 结构默认有一个隐式的无参数构造函数(由编译器自动生成),会将所有字段初始化为默认值(如 int 为 0,bool 为 false)。

  • 开发者不能自定义无参数构造函数(编译报错),但可定义带参数的构造函数,且必须在其中初始化所有字段

    // 错误示例:不能自定义无参数构造函数
    public struct BadStruct
    {
        public BadStruct() { } // 编译报错:结构不能包含显式的无参数构造函数
    }
    
    // 正确示例:带参数构造函数必须初始化所有字段
    public struct GoodStruct
    {
        private int _a;
        private string _b;
    
        public GoodStruct(int a, string b)
        {
            _a = a;
            _b = b; // 必须初始化所有字段,否则编译报错
        }
    }
    3. 不能被继承(密封类型)
  • 结构是 sealed(密封)的,无法作为基类被其他结构或类继承,但可以实现接口
    // 定义接口
    public interface IMovable
    {
        void Move(int x, int y);
    }
    
    // 结构实现接口
    public struct Point : IMovable
    {
        private int _x;
        private int _y;
    
        public Point(int x, int y)
        {
            _x = x;
            _y = y;
        }
    
        // 实现接口方法
        public void Move(int deltaX, int deltaY)
        {
            _x += deltaX;
            _y += deltaY;
        }
    }
    4. 可以包含的成员

结构可包含与类类似的成员,但有一些限制:

  • 允许:字段、属性、方法、构造函数(带参数)、索引器、事件、嵌套类型等。
  • 不允许:无参数构造函数、析构函数(~StructName())、protected 修饰符(因不能继承)。
5. 初始化方式

结构的实例化有两种方式:

  • 使用 new 关键字:调用构造函数,初始化所有字段。

  • 直接赋值:不使用 new,但必须先为所有字段赋值才能使用(否则编译报错)。

    // 方式1:使用new调用构造函数
    Point p1 = new Point(1, 2);
    
    // 方式2:直接赋值(必须初始化所有字段)
    Point p2;
    p2.X = 3; // 错误:未初始化所有字段,不能使用
    p2.Y = 4; 
    
    // 正确:先赋值所有字段,再使用  而不是赋值属性
    Point p3;
    p3.x = 3;
    p3.y = 4;
    Console.WriteLine(p3); // 输出:(3, 4)
    四、结构与类的核心区别
    对比项结构(struct)类(class)
    类型值类型(存储在栈上)引用类型(存储在堆上)
    赋值行为复制所有数据(值传递)复制引用地址(引用传递)
    继承不能被继承,可实现接口可继承,可实现接口
    构造函数不能自定义无参数构造函数可自定义无参数构造函数
    默认初始化隐式无参构造函数初始化所有字段为默认值无参构造函数需手动定义(否则默认 null)
    析构函数不允许定义允许定义(用于释放非托管资源)
    适用场景小型数据(如坐标、颜色、数值对)复杂对象(如 Person、Order)
    五、结构的使用场景

结构适合存储小型、简单、数据优先的实体,典型场景:

  1. 存储坐标或位置:如 Point(x,y)、Rectangle(宽、高、左上角坐标)。
  2. 表示数值类型组合:如 Currency(金额、货币单位)、DateTime(年、月、日)。
  3. 轻量级数据传输:如方法返回多个值时,用结构封装比用类更高效。

示例:系统内置结构

C# 中很多基础类型都是结构,例如:

  • intSystem.Int32)、doubleSystem.Double
  • DateTimeTimeSpan
  • PointRectangle(System.Drawing 命名空间)
六、结构的性能考量
  • 优势:作为值类型,结构在栈上分配和释放速度快,适合频繁创建和销毁的小型数据。
  • 注意点
  • 避免定义大型结构(如包含多个字段或大数组),否则复制时会占用大量栈内存,导致性能下降(此时应改用类)。

  • 结构作为方法参数传递时,默认是值传递(复制完整数据),若结构较大,建议用 ref 或 in 关键字传递引用,减少复制开销:

    // 用ref传递结构引用(避免复制)
    void ModifyPoint(ref Point p)
    {
        p.X = 100;
    }
    
    Point p = new Point(1, 2);
    ModifyPoint(ref p); // 直接修改原结构,不复制
    Console.WriteLine(p); // 输出:(100, 2)
    七、常见问题解答
  • Q:结构可以为 null 吗?
  • A:默认不能。但可通过可空值类型Nullable<StructName> 或 StructName?)让结构为 null:
    Point? p = null; // 可空结构,p.HasValue 为 false
    p = new Point(1, 2); // 赋值后 p.HasValue 为 true

 Q:结构的字段可以初始化默认值吗?

A:在 C# 10 及以上版本中,允许直接为结构的字段设置默认值:

public struct Point
{
    private int _x = 0; // 允许(C# 10+)
    private int _y = 0;
}

Q:结构和类如何选择?

A:遵循 “大小原则”:

  • 数据量小(字段少且类型简单)→ 用结构;
  • 数据量大或需要继承、复杂行为 → 用类。
八、完整案例:结构的综合使用
using System;

// 定义表示颜色的结构(ARGB值)
public struct Color : IEquatable<Color>
{
    // 字段(带默认值,C# 10+支持)
    private byte _alpha = 255;
    private byte _red;
    private byte _green;
    private byte _blue;

    // 构造函数(初始化所有字段)
    public Color(byte red, byte green, byte blue)
    {
        _red = red;
        _green = green;
        _blue = blue;
        // _alpha 有默认值,无需显式初始化
    }

    public Color(byte alpha, byte red, byte green, byte blue)
    {
        _alpha = alpha;
        _red = red;
        _green = green;
        _blue = blue;
    }

    // 属性
    public byte Alpha => _alpha;
    public byte Red => _red;
    public byte Green => _green;
    public byte Blue => _blue;

    // 方法:判断是否与另一个Color相等
    public bool Equals(Color other)
    {
        return _alpha == other._alpha &&
               _red == other._red &&
               _green == other._green &&
               _blue == other._blue;
    }

    // 重写ToString()
    public override string ToString()
    {
        return $"ARGB({_alpha}, {_red}, {_green}, {_blue})";
    }
}

class Program
{
    static void Main()
    {
        // 创建结构实例
        Color red = new Color(255, 0, 0);
        Color transparentRed = new Color(128, 255, 0, 0);

        // 赋值(值复制)
        Color redCopy = red;
        Console.WriteLine(redCopy); // 输出:ARGB(255, 255, 0, 0)

        // 比较相等性
        Console.WriteLine(red.Equals(transparentRed)); // 输出:False

        // 可空结构
        Color? nullColor = null;
        Console.WriteLine(nullColor.HasValue); // 输出:False
    }
}

总结

结构是 C# 中高效的值类型数据容器,适合存储小型、简单的数据。其核心特性包括:值类型存储、不能继承、无自定义无参构造函数等。使用时需根据数据大小和复杂度,在结构与类之间合理选择,以优化程序性能。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐