C#教程

关注公众号 jb51net

关闭
首页 > 软件编程 > C#教程 > C# 值类型

C# 中值类型的实现示例

作者:叫我少年

值类型和引用类型是 C# 类型系统的两大支柱,值类型变量"直接装数据",引用类型变量"装数据地址",下面就来详细的介绍一下值类型的使用和引用类型的对比实现

值类型和引用类型是 C# 类型系统的两大支柱。类型变量"直接装数据",引用类型变量"装数据地址"——这看似简单的差别,却深刻影响着赋值、传参、方法返回的行为。这篇来彻底拆解。

  1. 核心行为:值类型复制的是数据实例本身
  2. 重要警告:值类型包含引用类型成员时的"浅复制"陷阱
  3. 值类型的三种种类:结构、枚举、联合声明
  4. 简单类型的特殊性:文字、常量、编译期求值
  5. ​​struct​​ 约束与 ​System.Enum​​ 约束

一、核心行为:赋值即复制

值类型的变量直接包含类型实例,赋值时复制整个实例

 public struct MutablePoint
 {
     public int X;
     public int Y;
 ​
     public MutablePoint(int x, int y) => (X, Y) = (x, y);
     public override string ToString() => $"({X}, {Y})";
 }
 ​
 var p1 = new MutablePoint(1, 2);
 var p2 = p1;       // 复制整个结构体
 p2.Y = 200;        // 只影响 p2
 ​
 Console.WriteLine(p1);  // (1, 2)  ✅ p1 不受影响
 Console.WriteLine(p2);  // (1, 200)

方法传参也是复制:

 static void MutateAndDisplay(MutablePoint p)
 {
     p.X = 100;    // 改的是副本
     Console.WriteLine(p);  // (100, 200)
 }
 ​
 MutateAndDisplay(p2);
 Console.WriteLine(p2);  // (1, 200)  ✅ 原值不受影响

划重点: struct​ 默认按值传递——方法内部对参数的修改不会影响调用方的原始变量。这与 class 的行为正好相反。

二、重要陷阱:值类型包含引用类型成员

当结构体内部嵌套了引用类型(如 List<string>​),复制行为就变得有意思了——值类型实例被复制,但内部的引用类型只复制了引用(地址)

 public struct TaggedInteger
 {
     public int Number;
     private List<string> tags;
 ​
     public TaggedInteger(int n)
     {
         Number = n;
         tags = new List<string>();
     }
 ​
     public void AddTag(string tag) => tags.Add(tag);
     public override string ToString() => $"{Number} [{string.Join(", ", tags)}]";
 }
 ​
 var n1 = new TaggedInteger(0);
 n1.AddTag("A");
 Console.WriteLine(n1);  // 0 [A]
 ​
 var n2 = n1;            // 复制结构体
 n2.Number = 7;          // ✅ 只改 n2 的 Number(值类型成员)
 n2.AddTag("B");         // ⚠️ tags 是 List<string>,n1 和 n2 共享同一个 List
 ​
 Console.WriteLine(n1);  // 0 [A, B]  ← n1 也被影响了!
 Console.WriteLine(n2);  // 7 [A, B]
操作​n1.Number​n1.tags​n2.Number​n2.tags
初始0List["A"]
​n2 = n10List["A"]0同一个List["A"]
​n2.Number = 70List["A"]7同一个List["A"]
​n2.AddTag("B")0List["A", "B"]7同一个List["A", "B"]

【提示】 官方明确建议:尽量使用不可变值类型。如果结构体包含引用类型成员,一定要清楚这种"共享引用"的语义——这往往是 bug 的来源。

三、值类型的种类

C# 中的值类型有三种:

3.1 结构类型(struct)

 public struct Point { public int X; public int Y; }

3.2 枚举类型(enum)

 public enum Color { Red, Green, Blue }

3.3 联合声明(union)

定义一组封闭的事例类型,值可以表示。这是 .NET 6+ 引入的特性。

3.4 可为 null 的值类型(T?)

 int? nullableInt = null;      // Nullable<int>
 bool? nullableBool = null;    // Nullable<bool>

​T?​ 表示 T​ 的所有值 + null​。不能为 null​ 的值类型变量无法赋 null。

3.5 泛型约束

 // struct 约束:类型参数必须是值类型(struct 或 enum)
 public class MyClass<T> where T : struct { }
 ​
 // System.Enum 约束:类型参数必须是枚举类型
 public class MyClass<T> where T : System.Enum { }

四、内置值类型(简单类型)

C# 提供以下内置值类型,也称为简单类型:

类别类型
整型​byte​,sbyte​,short​,ushort​,int​,uint​,long​,ulong​,nint​,nuint
浮点型​float​,double​,decimal
布尔​bool
字符​char

所有简单类型都是结构类型,但与其他结构类型不同,它们允许一些额外操作:

操作简单类型自定义结构类型
文字常量✅'A'​、2001​、12.34m
​​const​​声明✅const int Max = 100;
编译期求值✅ 常量表达式在编译时求值
 // 简单类型特有的操作
 const int MaxItems = 100;           // ✅ struct 是简单类型
 const decimal TaxRate = 0.05m;      // ✅
 ​
 // 以下 ❌ 编译错误
 public struct Point { public int X; public int Y; }
 // const Point Origin = new Point();  // 不能声明结构类型的常量

五、值元组

值元组(ValueTuple<T1, T2>​)是值类型,但不是简单类型。

var point = (X: 10, Y: 20);  // ValueTuple<int, int>

六、对比:值类型 vs 引用类型

维度值类型引用类型
变量内容数据本身数据地址(引用)
赋值行为复制整个实例复制引用(地址)
默认传参按值传递(副本)按值传递引用(同上仍是副本)
​null赋值❌ 不可(除非T?)
存储位置栈/内联
继承❌struct不能继承✅class可以
性能小数据快(无 GC 压力)大数据好(引用传递)

最后: 值类型的行为可以用一句话概括——值类型变量就是数据本身。赋值即复制、传参即复制、包含引用类型成员时要小心"共享引用"。这些规则虽然简单,但组合起来却影响着日常编码中的每一个细节。记住官方建议:优先用不可变值类型,代码会更可靠。

到此这篇关于C# 中值类型的实现示例的文章就介绍到这了,更多相关C# 值类型内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文