切换导航
简历
工具
演示
查询
导航菜单
服务端
30
前端
114
数据库
82
进阶开发
8
桌面程序
2
QA
15
软件使用
36
规范协议
5
操作系统
11
AI
0
复用资源
6
硬件
0
标签
C# 基础
Mybatis
ADO.NET
ASP.NET
Python 基础
Linq
Web Forms
.NET
Spring
SpringBoot
SpringCloud
.NET Core 中间件
Maven
.NET 类库
ASP.NET MVC
EF
NHibernate
WebApi
Java
外部链接
.NET Core SDK和运行时安装包
淘宝 NPM 镜像
查询网
Tables Generator线上表格转换工具
引用cdn在线脚本和样式
首页
服务端
C# 元组类型介绍和使用
[TOC] ## 1. 什么是元组类型 元组类型是值类型,元组功能使用简洁的语法将多个数据元素分组成一个轻型数据结构,元组类型可以根据元素成员的数量叫做二元组、三元组…… 下面一个简单的示例加强下概念的理解 ```csharp // TupleBase() 输出:TupleBase:[(1, 曹孟德)],[1],[曹孟德] public static string TupleBase() { (int, string) t1 = (1, "曹操"); t1.Item2 = "曹孟德"; return $"{new StackTrace(true).GetFrame(0).GetMethod().Name}:[{t1.ToString()}],[{t1.Item1}],[{t1.Item2}]"; } ``` > **Tips:** > + `Item*` 是元组类型固有属性,其中的 `*` 是由这个元组类型有多少个类型参数决定的,上诉示例中有两个参数,因此就有 Item1 和 Item2 两个固有属性,如果有更多的参数,那就会将元组的值分配到对应的 item 上 > + 元组类型不能自己定义方法,但可以调用.NET的方法,例如上诉示例中 t1.ToString() > + 元组元素是公共字段,因此元组为可变的值类型,例如上述示例中 t1.Item2 = "曹孟德" ## 2. 元组类型声明方式 元组类型声明的方式有很多种,主要有以下几类 ### 2.1. 类型声明 只声明了数据成员的类型,按声明的位置赋值,如果对应位置上的类型和赋值不一致会提示类型错误的提示 ```csharp // TupleBase() 输出:TupleBase:[(1, 曹操)],[1],[曹操] public static string TupleBase() { (int, string) t1 = (1, "曹操"); //定义了一个元组类型被赋值 return $"TupleBase:[{t1.ToString()}],[{t1.Item1}],[{t1.Item2}]"; } ``` ### 2.2. 变量声明 给数据成员直接声明为变量,这样在使用元组类型的时候更加的直观,可以将它当成对象成员来使用 ```csharp // TupleDefine() 输出:TupleDefine:[1],[曹操] public static string TupleDefine() { (int id, string name) t1 = (1, "曹操"); return $"TupleDefine:[{t1.id}],[{t1.name}]"; } ``` ### 2.3. var声明 当不确定元组类型结构时,可以使用 var 来声明,最终的元组类型结构由后续的初始化决定 ```csharp // TupleVar() 输出:TupleVar:[1],[曹操],[男],[东汉丞相] public static string TupleVar() { var t1 = (1, "曹操", "男", "东汉丞相"); return $"{new StackTrace(true).GetFrame(0).GetMethod().Name}:[{t1.Item1}],[{t1.Item2}],[{t1.Item3}],[{t1.Item4}]"; } ``` ### 2.4. 字段声明 字段声明的书写格式和对象类似,只是将大括号换成了小货号,使用字段声明必须要为字段赋值,否则会提示错误,找不到变量 ```csharp // TupleField() 输出:TupleField:[2],[刘备],[1],[曹操] public static string TupleField() { var t2 = (t1: 1, name: "曹操"); int id = 2; string name = "刘备"; var t1 = (id, name); return $"{new StackTrace(true).GetFrame(0).GetMethod().Name}:[{t1.Item1}],[{t1.Item2}],[{t2.Item1}],[{t2.Item2}]"; } ``` > **Tips :** > <mark>CS8126: 任何位置都不允许使用元组元素名称”ToString”。</mark>,当使用字段声明时,如果使用了元组类型的成员名称就会提示这类错误,因此不可将元组类型成员名称作为字段名称 字段类型声明中也时可以混合非字段的声明,例如下面的示例,没有使用字段的元组成员,还是可以使用 `Item*` 来回去对应位置的值,但需要注意成员所在的位置顺序 ```csharp // TupleFieldExtend() 输出:TupleFieldExtend:[1],[曹操],[男],[东汉丞相] public static string TupleFieldExtend() { var t1 = (id: 1, name: "曹操", "男", "东汉丞相"); return $"{new StackTrace(true).GetFrame(0).GetMethod().Name}:[{t1.id}],[{t1.name}],[{t1.Item3}],[{t1.Item4}]"; } ``` >**注意:**<span style="color:red">在编译时,编译器会将非默认字段名称替换为相应的默认名称。 因此,显式指定或推断的字段名称在运行时不可用</span> ## 3. 元组赋值 C# 支持满足以下两个条件的元组类型之间的赋值: + 两个元组类型有相同数量的元素 + 对于每个元组位置,右侧元组元素的类型与左侧相应的元组元素的类型相同或可以隐式转换为左侧相应的元组元素的类型 元组元素是按照元组元素的顺序赋值的。 元组字段的名称会被忽略且不会被赋值,如下面的示例所示: ```csharp // TupleValuation() 输出:TupleValuation:t1 = [2],[刘备],[男],[汉室皇叔],t3 = [2],[刘备],[男],[汉室皇叔] public static string TupleValuation() { (int, string, string, string) t1 = (1, "曹操", "男", "东汉丞相"); (int, string, string, string) t2 = (2, "刘备", "男", "汉室皇叔"); t1 = t2; (int id, string name, string sex, string summary) t3 = (3, "孙策", "男", "江东猛虎"); t3 = t1; return $"{new StackTrace(true).GetFrame(0).GetMethod().Name}:t1 = [{t1.Item1}],[{t1.Item2}],[{t1.Item3}],[{t1.Item4}],t3 = [{t3.id}],[{t3.name}],[{t3.sex}],[{t3.summary}]"; } ``` ### 3.1. 几类常见的元组赋值 + **在括号外使用 var 关键字来声明隐式类型变量,并让编译器推断其类型** ```csharp // TupleVarValuation() 输出:TupleVarValuation:[1],[曹操],[男],[东汉丞相] public static string TupleVarValuation() { var t1 = (1, "曹操", "男", "东汉丞相"); var (id, name, sex, summary) = t1; return $"{new StackTrace(true).GetFrame(0).GetMethod().Name}:[{id}],[{name}],[{sex}],[{summary}]"; } ``` + **在括号内显式声明每个变量的类型** ```csharp (int, string, string, string) t1 = (1, "曹操", "男", "东汉丞相"); // 显式声明变量 (int id, string name, string sex, string summary) t3 = (3, "孙策", "男", "江东猛虎"); t3 = t1; return $"[{t3.id}],[{t3.name}],[{t3.sex}],[{t3.summary}]"; // 输出:[1],[曹操],[男],[东汉丞相] ``` + **在括号内显式声明一些类型,隐式声明其他类型(使用 var)** ```csharp var t = (1, "曹操", "男", "东汉丞相"); (int id, var name, var sex, var summary) = t; return $"[{id}],[{name}],[{sex}],[{summary}]"; // 输出:[1],[曹操],[男],[东汉丞相] ``` + **使用现有变量** ```csharp var name = string.Empty; var summary = string.Empty; var t = ("曹操", "东汉丞相"); (name, summary) = t; return $"[{name}],[{summary}]"; // 输出:[曹操],[东汉丞相] ``` > **Tips:**元组类型赋值时,左右两边的元组成员数量需要一致,如果不是隐式声明,类型也需要保持一致 ### 3.2. 元组的解构 将元组的各个元素赋值给不同的变量的过程,这个变量可以是一个新的元组,但这个元组必须与被解构的元组保持数量一致,类型一致(或可以隐式转换),也可以是已经定义好的变量,下面是常见的几种解构示例 + **可以在括号内显式声明每个字段的类型** ```csharp // GetInfo() 函数返回一个四元组 (int id, string name, string sex, string summary) public static void Main() { (string id, string name, string sex, string summary) = GetInfo(); Console.WriteLine($"{[{id}],[{name}],[{sex}],[{summary}]") } ``` + **可使用 var 关键字,以便 C# 推断每个变量的类型。 可将 var 关键字放在括号外,也可以放在括号内** ```csharp // GetInfo() 函数返回一个四元组 (int id, string name, string sex, string summary) public static void Main() { // 将 var 放在括号外 var (id, name, sex, summary) = GetInfo(); Console.WriteLine($"{[{id}],[{name}],[{sex}],[{summary}]") // 将 var 放在括号内,括号内的 var 可以与显式的变量一起混用 (int id1,var name1,var sex1,var summary1) = GetInfo(); Console.WriteLine($"{[{id1}],[{name1}],[{sex1}],[{summary1}]") } ``` + **元组解构到已声明的变量中** ```csharp // GetInfo() 函数返回一个四元组 (int id, string name) public static void Main() { int id = 0; string name = string.Empty; (id, name) = GetInfo(); Console.WriteLine($"{[{id}],[{name}]") } ``` > **Tips:** 从 C# 10 开始,可在析构中混合使用变量声明和赋值,例如上面示例中,没有提前声明 string name,可以直接写成 (id,string name) = GetInfo(); + **使用弃元的元组:**解构元组时,通常只需要关注某些元素的值。 可以利用 C# 对弃元的支持,弃元是一种仅能写入的变量,且其值将被忽略。 在赋值中,通过下划线字符 `_` 指定弃元。 可弃元任意数量的值,且均由单个弃元 `_` 表示,下面的示例就是舍弃了id和sex元素 ```csharp // GetInfo() 函数返回一个四元组 (int id, string name, string sex, string summary) public static void Main() { (_, string name, _, string summary) = GetInfo(); Console.WriteLine($"{[{name}],[{summary}]") } ``` ### 3.3. 元组的赋值和元组解构的区别 元组的解构和赋值是两种不同的操作方式。解构用于将元组中的元素分配给其他变量,而赋值用于改变元组的引用或修改元组中的元素值 ## 4. 元组相等 元组类型支持 == 和 != 运算符。 这些运算符按照元组元素的顺序将左侧操作数的成员与相应的右侧操作数的成员进行比较 ```csharp (int a, byte b) left = (5, 10); (long a, int b) right = (5, 10); Console.WriteLine(left == right); // output: True Console.WriteLine(left != right); // output: False var t1 = (A: 5, B: 10); var t2 = (B: 5, A: 10); Console.WriteLine(t1 == t2); // output: True Console.WriteLine(t1 != t2); // output: False ``` > 如示例所示,== 和 != 操作不会考虑元组字段名称。 **同时满足以下两个条件时,两个元组可比较:** + 两个元组具有相同数量的元素。 例如,如果 t1 和 t2 具有不同数目的元素,t1 != t2 则不会进行编译。 + 对于每个元组位置,可以使用 == 和 != 运算符对左右侧元组操作数中的相应元素进行比较。 例如,(1, (2, 3)) == ((1, 2), 3) 不会进行编译,因为 1 不可与 (1, 2) 比较。 == 和 != 运算符将以短路方式对元组进行比较。 也就是说,一旦遇见一对不相等的元素或达到元组的末尾,操作将立即停止。 但是,在进行任何比较之前,将对所有元组元素进行计算,如以下示例所示: ```csharp Console.WriteLine((Display(1), Display(2)) == (Display(3), Display(4))); int Display(int s) { Console.WriteLine(s); return s; } // Output: // 1 // 2 // 3 // 4 // False ``` ## 5. 元组类型的应用 ### 5.1. 结合模式应用 示例循环访问多个整数,并输出可被 3 整除的整数。 它解构 Int32.DivRem 的元组结果,并与 Remainder 0 进行匹配 ```csharp public static string TupleExample() { string msg = ""; for (int i = 4; i < 20; i++) { var r = Math.DivRem(i, 3); if (r is (Quotient: int q, Remainder: 0)) { msg += $"{i} is divisible by 3, with quotient {q}\r\n"; } } return msg; } ``` ### 5.2. 元组作为 out 参数 一些匹配函数需要返回 out 参数时,可以搭配元组类型使用,下面的示例中就是将元组类型作为 out 参数 ```csharp public static string TupleOutExample() { string msg = ""; var limitsLookup = new Dictionary<int, (int Min, int Max)>() { [2] = (4, 10), [4] = (10, 20), [6] = (0, 23) }; if (limitsLookup.TryGetValue(4, out (int Min, int Max) limits)) { msg = $"Found limits: min is {limits.Min}, max is {limits.Max}"; } return msg; // 输出:Found limits: min is 10, max is 20 } ``` [========] > 参看资料: [《元组类型(C# 参考)》](https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/builtin-types/value-tuples) [《析构元组和其他类型》](https://learn.microsoft.com/zh-cn/dotnet/csharp/fundamentals/functional/deconstruct)对应的英文[《Deconstructing tuples and other types》](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/deconstruct) [《在匿名类型和元组类型之间进行选择》](https://learn.microsoft.com/zh-cn/dotnet/standard/base-types/choosing-between-anonymous-and-tuple)
扫一扫手机看
点击排行
windows 文件资源管理器使用
Layui 弹窗open使用说明
SQL Server 中字符串函数使用
C# 使用QQ邮箱代理做邮件接收功能
SQL Server关于apply的两种形式cross apply 和 outer apply
ASP.NET MVC 模型的自定义验证
nvm安装及常用命令
SQL Server 中数据类型转换
SQL Server 中WITH AS短语的使用
IE浏览器下AJAX缓存问题导致数据不更新的解决办法
订阅号