C#中的Struct还是Class:新手与专家的永恒争论
\n\n您是否曾想过,为什么C#中有两种非常相似的方式来创建自己的数据类型——class和struct?为什么在Stack Overflow上,问题“When should I use a struct rather than a class in C#?”会获得数千次浏览和数百个回答?原因在于,在看似简单的表象背后,隐藏着一个深刻的细微差别,它既可以加速您的程序,也可能使其成为错误的温床。
在本文中,我们将分析struct与class的区别,何时应该使用struct,以及何时最好忘记它的存在。没有晦涩的术语,只有生动的示例和实用的建议。我们开始吧!
\n\n主要区别:栈还是堆?
\n\n需要理解的最重要的一点是——值类型(value types)和引用类型(reference types)。
\n\n- \n
- Class——引用类型。对象存储在托管堆(heap)中,变量只包含对该对象的引用(地址)。 \n
- Struct——值类型(value type)。变量直接在栈(stack)上或另一个对象内部存储数据本身。 \n
听起来很抽象?让我们举个例子。假设数据是一本书。Class是书名和它在图书馆中的书架编号。Struct是您随身携带在口袋里的书本身。当您将class传递给方法时,您传递的是书架编号。当您传递struct时,您交出的是书的副本。
\n\n代码示例:class vs struct
\n\npublic 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:
1. 数据小而简单
\nStruct非常适合小型数据集(建议不超过16-24字节)。例如,点、颜色、向量、复数、坐标。
\n\npublic 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\n2. 对象生命周期短且不可变
\n如果您的数据是不可变(immutable)的值,struct是一个很好的选择。例如,地图上的坐标:您不会修改一个点,而是创建一个新的点。
\n\npublic 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