C#结构(struct)与类(class)的区别详解:值类型与引用类型对比

拾光
拾光
发布时间:2026-10-13 | 4人浏览过
原创

在C#开发中,结构和类是两种核心的数据类型定义方式。类是引用类型,存储在堆内存中;而结构是值类型,直接存储数据内容。理解两者的差异对于性能优化和内存管理至关重要。

核心差异概览

虽然结构和类都可以包含数据成员(字段)和函数成员(方法),但它们在内存管理和语义上有本质区别:类实例的变量只保存对堆中对象的引用,而结构变量的变量直接包含数据本身。以下是具体的区别总结:

  1. 类型性质:结构是值类型,对结构变量赋值会创建该值的一个完整副本;类是引用类型,赋值仅复制引用地址。
  2. 默认值:结构的默认值不是 null,而是其所有字段按默认值初始化后的状态;类类型的变量若未实例化则为 null。
  3. 继承限制:结构不支持继承(除了隐式继承自 System.ValueType),因此不能声明为 protected 或 protected internal,也不能使用 virtual、abstract 修饰符,但可以实现接口。
  4. 构造函数限制:结构中不能声明无参数的实例构造函数;且在实例字段声明中不能含有初始值设定项(如 public int x = 1; 在结构中非法)。
  5. 析构函数:结构中不能声明析构函数,因为值类型不涉及垃圾回收器的复杂处理。

代码示例:值类型与引用类型的赋值行为

通过以下代码可以直观看到,当把一个结构变量赋给另一个变量时,修改其中一个不会影响另一个;而将类变量赋值后,修改会影响双方,因为它们指向同一个对象。

using System;
namespace StructAndClass
{
  struct SPoint
  {
    public int x, y;
    public SPoint(int x, int y)
    {
      this.x = x;
      this.y = y;
    }
  }
  class CPoint
    {
      public int x, y;
      public CPoint(int x, int y)
      {
        this.x = x;
        this.y = y;
      }
    }
  class Test
  {
    public static void Main()
    {
      // --- 结构测试 ---
      SPoint sp1 = new SPoint(2, 5);
      Console.WriteLine("结构/sp1初始值:");
      Console.WriteLine("sp1.x={0}", sp1.x);
      
      SPoint sp2 = sp1; // 此时sp2是sp1的完整副本
      Console.WriteLine("sp1=sp2后:");
      Console.WriteLine("sp1.x={0}", sp1.x);
      Console.WriteLine("sp2.x={0}", sp2.x); // 输出应为 2
      
      sp1.x = 5; // 修改sp1
      Console.WriteLine("再次改变sp1的值后:");
      Console.WriteLine("sp1.x={0}", sp1.x); // 输出 5
      Console.WriteLine("sp2.x={0}", sp2.x); // 输出仍为 2,互不影响
      
      Console.WriteLine("============================");
      
      // --- 类测试 ---
      CPoint cp1 = new CPoint(2,5);
      Console.WriteLine("类/cp1初始值:");
      Console.WriteLine("cp1.x={0}", cp1.x);
      
      CPoint cp2 = cp1; // cp2指向与cp1相同的堆内存对象
      Console.WriteLine("cp1=cp2后:");
      Console.WriteLine("cp1.x={0}", cp1.x);
      Console.WriteLine("cp2.x={0}", cp2.x); // 输出 2
      
      cp1.x = 5; // 修改cp1
      Console.WriteLine("再次改变cp1的值后:");
      Console.WriteLine("cp1.x={0}", cp1.x); // 输出 5
      Console.WriteLine("cp2.x={0}", cp2.x); // 输出也变为 5,因为是同一对象
      
      Console.ReadKey();
    }
  }
}

结构的初始化与构造规则

对于结构,即使不使用 new 运算符声明,变量也是有效的(但在访问前必须确保所有字段已赋值)。结构始终包含一个默认的无参数构造函数,但该构造函数会将所有字段设为默认值且不可被覆盖。

如果在结构中自定义了带参构造函数,编译器不会再生成默认的无参构造函数供外部调用。在使用自定义构造函数前,必须显式为所有字段赋值。

struct DC
  {
    public int x, y;
    public int X
    {
      set { x = value; }
      get { return x; }
    }
    public int Y
    {
      set { y = value; }
      get { return y; }
    }
    // 自定义构造函数
    public DC(int x,int y)
    {
      this.x = x;
      this.y = y;
    }
  }

在上面的示例中使用 DC dc=new DC(); 会编译失败,因为没有匹配的无参构造函数。必须先赋值再访问:

class Test
  {
    public static void Main()
    {
      DC dc; // 有效但未初始化
      dc.x = 3; // 合法
      dc.y = 5; // 合法
      Console.WriteLine("已经对x,y初始化后:");
      Console.WriteLine("此时可以访问dc.X={0}的值",dc.X);
      
      // 尝试不赋值直接访问属性会报错
      RDC rdc;
       rdc.y = 5; // 合法
       // rdc.X = 3; // 如果X的set内部访问未初始化的y或类似逻辑可能会出错,具体视实现而定
       // 但在某些严格场景下,如rdr没有完全初始化就调用涉及this的方法会报错
    }
  }

关于 this 的特殊说明

在结构的实例构造函数内,this 相当于一个 out 参数,必须在构造函数结束前对所有字段明确赋值,否则编译错误。在普通实例方法中,this 相当于 ref 参数,意味着可以直接修改结构体的状态。

// 错误示例
struct BadStruct
  {
    public int x, y;
    public BadStruct(int x, int y)
    {
      // X=x; // 错误!如果X是属性且依赖y,或者没有给所有字段赋值
      this.x = x; // 正确:直接赋值字段
      this.y = y; 
    }
  }
以上就是C#结构与类的详细对比内容,更多关于C#基础语法的资料请关注本站其它相关文章!
相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表