C#中的Struct还是Class:新手该如何选择?

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

C#中的Struct还是Class:新手与专家的永恒争论

\n\n

您是否曾想过,为什么C#中有两种非常相似的方式来创建自己的数据类型——class和struct?为什么在Stack Overflow上,问题“When should I use a struct rather than a class in C#?”会获得数千次浏览和数百个回答?原因在于,在看似简单的表象背后,隐藏着一个深刻的细微差别,它既可以加速您的程序,也可能使其成为错误的温床。

\n\n

在本文中,我们将分析struct与class的区别,何时应该使用struct,以及何时最好忘记它的存在。没有晦涩的术语,只有生动的示例和实用的建议。我们开始吧!

\n\n

主要区别:栈还是堆?

\n\n

需要理解的最重要的一点是——值类型(value types)和引用类型(reference types)。

\n\n
    \n
  • Class——引用类型。对象存储在托管堆(heap)中,变量只包含对该对象的引用(地址)。
  • \n
  • Struct——值类型(value type)。变量直接在栈(stack)上或另一个对象内部存储数据本身。
  • \n
\n\n

听起来很抽象?让我们举个例子。假设数据是一本书。Class是书名和它在图书馆中的书架编号。Struct是您随身携带在口袋里的书本身。当您将class传递给方法时,您传递的是书架编号。当您传递struct时,您交出的是书的副本。

\n\n

代码示例:class vs struct

\n\n
public class PointClass\n{\n    public int X { get; set; }\n    public int Y { get; set; }\n\n    public PointClass(int x, int y)\n    {\n        X = x;\n        Y = y;\n    }\n}\n\npublic struct PointStruct\n{\n    public int X { get; set; }\n    public int Y { get; set; }\n\n    public PointStruct(int x, int y)\n    {\n        X = x;\n        Y = y;\n    }\n}\n\n// 使用\nvar pointClass = new PointClass(10, 20);\nvar pointStruct = new PointStruct(10, 20);\n\n// 传递给方法\nvoid ModifyPointClass(PointClass p) { p.X = 100; }\nvoid ModifyPointStruct(PointStruct p) { p.X = 100; }\n\nModifyPointClass(pointClass);   // pointClass.X 变为 100\nModifyPointStruct(pointStruct); // pointStruct.X 保持为 10 — 副本!\n
\n\n

看到了吗?Struct在传递时会被复制。这就是决定何时使用它的关键区别。

\n\n

何时struct是您的朋友:3个铁定的情况

\n\n

微软建议在您的数据类型满足所有以下条件时使用struct:

\n\n

1. 数据小而简单

\n

Struct非常适合小型数据集(建议不超过16-24字节)。例如,点、颜色、向量、复数、坐标。

\n\n
public struct RgbColor\n{\n    public byte R { get; }\n    public byte G { get; }\n    public byte B { get; }\n\n    public RgbColor(byte r, byte g, byte b)\n    {\n        R = r;\n        G = g;\n        B = b;\n    }\n}\n
\n\n

为什么?因为小的struct复制速度快,并且在栈上存储可以减轻垃圾回收器(GC)的负担。

\n\n

2. 对象生命周期短且不可变

\n

如果您的数据是不可变(immutable)的值,struct是一个很好的选择。例如,地图上的坐标:您不会修改一个点,而是创建一个新的点。

\n\n
public struct GeoLocation\n{\n    public double Latitude { get; }\n    public double Longitude { get; }\n\n    public GeoLocation(double lat, double lon)\n    {\n        Latitude = lat;\n        Longitude = lon;\n    }\n\n    public GeoLocation MoveNorth(double meters)\n    {\n        // 返回一个新实例,不修改旧实例\n        return new GeoLocation(Latitude + meters / 111111.0, Longitude);\n    }\n}\n
\n\n

3. 性能至关重要

博客

书籍推荐