VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > 编程开发 > c#编程 >
  • 元组与记录——C#编写简化数据传输对象

第一部分:C#基础入门
9.元组与记录——简化数据传输对象
实例介绍
在日常开发中,数据传输对象(DTO)是连接不同模块的常用工具,用于传递结构化数据(如API返回的用户信息、数据库查询结果)。传统的DTO实现需要定义类、编写属性、构造函数,甚至重写Equals和GetHashCode以支持值相vb.net教程C#教程python教程SQL教程access 2010教程
等性,样板代码冗余且繁琐。
C# 7+引入的元组(Tuple)和C#9+引入的记录(Record),为简化DTO提供了高效解决方案:
元组:轻量级,支持快速创建临时数据结构,无需定义类;
记录:面向不可变数据,自动生成值相等性、解构方法和ToString,大幅减少样板代码。
本节通过“用户信息传输”场景,对比传统DTO、元组、记录的实现差异,带你掌握这两种工具的核心用法。
需求分析
设计一个用户信息传输工具,核心需求如下:
1.减少样板代码:避免手动编写属性、构造函数、相等性方法;
2.支持不可变数据:防止传输过程中意外修改数据;
3.方便解构:快速提取数据成员,无需逐个访问属性;
4.值相等性:基于数据内容比较两个对象是否相等(而非引用地址);
5.可读性:成员有明确命名,避免“Item1、Item2”的模糊表述。
代码实现

  1. 传统DTO实现(对比基准)
    csharp
	using System;
	
	// 传统用户DTO类
	public class UserDTO
	{
	// 不可变属性(C#9+支持init修饰符)
	public string Name { get; init; }
	public int Age { get; init; }
	public string Email { get; init; }
	
	// 构造函数
	public UserDTO(string name, int age, string email)
	{
	Name = name;
	Age = age;
	Email = email;
	}
	
	// 重写Equals:基于属性值比较
	public override bool Equals(object obj)
	{
	return obj is UserDTO dto &&
	Name == dto.Name &&
	Age == dto.Age &&
	Email == dto.Email;
	}
	
	// 重写GetHashCode:结合属性值生成哈希码
	public override int GetHashCode()
	{
	return HashCode.Combine(Name, Age, Email);
	}
	
	// 重写ToString:友好输出
	public override string ToString()
	{
	return $"UserDTO: Name={Name}, Age={Age}, Email={Email}";
	}
	}
  1. 元组实现(C#7+)
    csharp
	// 匿名值元组:可读性低,依赖位置访问
	var anonymousTuple = ("张三", 25, "zhangsan@example.com");
	Console.WriteLine(anonymousTuple.Item1); // 输出"张三"
	
	// 命名值元组:为成员命名,提升可读性
	var namedTuple = (Name: "张三", Age: 25, Email: "zhangsan@example.com");
	Console.WriteLine(namedTuple.Name); // 输出"张三"
	
	// 解构元组:快速提取所有成员
	var (name, age, email) = namedTuple;
	Console.WriteLine($"Name: {name}, Age: {age}, Email: {email}");
  1. 记录实现(C#9+)
    csharp
	// Positional Record:构造参数自动转为不可变属性
	public record UserRecord(string Name, int Age, string Email);
	
	// 测试记录特性
	var user1 = new UserRecord("张三", 25, "zhangsan@example.com");
	var user2 = new UserRecord("张三", 25, "zhangsan@example.com");
	
	// 1. 值相等性:默认基于属性值比较
	Console.WriteLine(user1.Equals(user2)); // True(传统类默认False)
	
	// 2. with表达式:修改部分属性(创建新实例,原实例不变)
	var updatedUser = user1 with { Age = 26 };
	Console.WriteLine(updatedUser.Age); // 26
	
	//3. 解构记录:自动生成Deconstruct方法
	var (rName, rAge, rEmail) = user1;
	Console.WriteLine($"Record: {rName}, {rAge}, {rEmail}");
	
	//4. ToString:自动生成友好输出
	Console.WriteLine(user1); // 输出"UserRecord { Name = 张三, Age =25, Email=zhangsan@example.com }"

逐行讲解

  1. 传统DTO类
    不可变属性:用init修饰符确保属性只能在构造或初始化时赋值,防止数据被意外修改;
    构造函数:手动初始化所有属性,保证数据完整性;
    Equals重写:对比所有属性值,实现值相等性(传统类默认是引用相等);
    GetHashCode重写:结合属性值生成哈希码,确保在哈希表(如Dictionary)中正确使用;
    ToString重写:输出友好的字符串表示,便于调试。
  2. 元组实现
    匿名值元组:用括号包裹值,成员通过Item1、Item2访问,适合临时、简单场景;
    命名值元组:在括号内为成员命名(如Name: "张三"),编译器自动生成对应属性,可读性显著提升;
    解构:通过var (name, age, email) = tuple语法快速提取所有成员,无需逐个访问。
  3. 记录实现
    Positional Record:用record关键字+构造参数定义,参数自动转为不可变属性(init-only);
    值相等性:记录默认重写Equals和GetHashCode,基于所有属性值比较,而非引用地址;
    with表达式:创建新实例并修改指定属性(原实例不变),符合不可变数据的设计原则;
    解构:自动生成Deconstruct方法,支持与元组相同的解构语法;
    ToString:自动生成包含所有属性的字符串,无需手动重写。
    基础知识拓展
  4. 元组类型对比
    C#中的元组分为两类,核心差异在于存储方式和用途:
    类型 语法示例 存储类型 适用场景
    ValueTuple<T1,...> (string, int, string) 值类型 临时数据传输、方法返回多值
    Tuple<T1,...> Tuple.Create("张三",25) 引用类型 兼容旧版本(C#7前),少用
    命名元组是ValueTuple的语法糖,编译器会生成对应类型,并为成员添加别名(如Name对应Item1)。

2. 记录vs传统类vs元组

三者的核心差异如下表:

特性 传统类(Class) 记录(Record) 元组(Tuple)
样板代码 多 少 无
不可变性 手动实现 默认支持 支持(值元组)
值相等性 需重写Equals 默认支持 默认支持
可读性 高 高 低/中
方法支持 支持 支持 不支持
继承 支持(类继承类) 支持(记录继承记录) 不支持
  1. 记录的高级特性
    Init-only属性:记录的属性默认是init修饰符,只能在构造或用with修改,确保数据不可变;
    继承:记录可以继承其他记录(如public record StudentRecord(string Name, int Age, string StudentId) : UserRecord(Name, Age));
    自定义方法:记录可以添加方法、事件等,如:
    csharp
	public record UserRecord(string Name, int Age, string Email)
	{
	public bool IsAdult() => Age >= 18;
	}

显式解构:手动重写Deconstruct方法,自定义解构逻辑:
csharp

	public record UserRecord(string Name, int Age, string Email)
	{
	public void Deconstruct(out string name, out int age)
	{
	name = Name.ToUpper();
	age = Age;
	}
	}
  1. 适用场景总结
    元组:适合临时、简单的数据传输(如方法返回多值、短期中间结果);
    记录:适合长期、稳定的DTO(如API返回数据、数据库实体);
    传统类:适合需要可变状态或复杂逻辑的业务对象。
    总结
    元组和记录是C#简化数据传输对象的利器,它们分别解决了不同场景的痛点:
    元组:以“无样板代码”为核心,快速构建临时数据结构;
    记录:以“不可变性+值相等性”为核心,替代传统DTO类,减少冗余代码。
    在实际开发中,应根据数据的生命周期和复杂度选择合适的工具:临时数据用元组,稳定DTO用记录,复杂业务逻辑用传统类。掌握这两种工具,能显著提升代码的简洁性和可读性。

 本站原创,转载请注明出处:https://www.xin3721.com/ArticlecSharp/c49431.html


相关教程