我的编程空间,编程开发者的网络收藏夹
学习永远不晚

C#中元组类型ValueTuple怎么用

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

C#中元组类型ValueTuple怎么用

这篇文章将为大家详细讲解有关C#中元组类型ValueTuple怎么用,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

System.Tuple 类型是在.NET 4.0中引入的,但是有两个明显的缺点:
(1) Tuple 类型是引用类型。
(2) 没有构造函数支持。

为了解决这些问题,C# 7 引入了新的语言功能以及新的类型。

现在,如果您需要从函数中返回两个值的合并结果,或者把两个值合并到一个哈希表中,可以使用System.ValueTuple类型并使用一个精短的语法来构造它们:

    // 构建元组实例    var tpl = (1, 2);                    // 在字典中使用元组    var d = new Dictionary<(int x, int y), (byte a, short b)>();         // 不同名称的元组是兼容的    d.Add(tpl, (a: 3, b: 4));         // 元组值的语义    if (d.TryGetValue((1, 2), out var r))    {        // 解构元组忽略第一个元素        var (_, b) = r;                            // 使用命名语法和定义名称        Console.WriteLine($"a: {r.a}, b: {r.Item2}");    }

System.ValueTuple 类型在.NET Framework 4.7中引入。但是您仍然可以在较低的框架版本中使用这个功能,这时候,您必须引用一个特殊的nuget包:System.ValueTuple。

  • 元组声明的语法与函数参数声明相似:(Type1 name1, Type2 name2)

  • 元组的构造语法类似于参数构造:(value1, optionalName: value2)

  • 两个元组具有相同的元素类型,但不同的名称是兼容(**):(int a, int b) = (1, 2)

  • 元组值的语义: (1,2).Equals((a: 1, b: 2))(1,2).GetHashCode() == (1,2).GetHashCode() 返回的值均是true

  • 元组不支持==!=。在github上有一个悬而未决的讨论:“支持==和!=元组类型”。

  • 元组可以被“解构”,但只能转换成“变量声明”,而不能“out var”或case语句中转换:var (x, y) = (1,2) - OK, (var x, int y) = (1,2) - OK, dictionary.TryGetValue(key, out var (x, y)) - not OK, case var (x, y): break; - not OK。

  • 元组是可变的:(int a, int b) x = (1,2); x.a++;.

  • 元组元素可以通过名称(如果提供的话)或通过通用名称Item1Item2等来访问。

我们马上就会明白上面几点。

元组名称

缺少用户定义的名称导致System.Tuple类型不常用。我们可以将System.Tuple用作一个精减方法的实现细节,但如果我们需要传递它,我更喜欢使用具有描述性属性名称的命名类型。新元组功能很好地解决了这个问题:可以为元组元素指定名称,而不像匿名类型,即使在不同的程序集中也可以使用这些名称。

C#编译器为方法签名中使用的每个元组类型指定了一个特殊的标记TupleElementNamesAttribute

TupleElementNamesAttribute标记非常特殊,不能在用户代码中直接使用。如果您尝试使用它,编译器会报出错误。

    public (int a, int b) Foo1((int c, int d) a) => a;     [return: TupleElementNames(new[] { "a", "b" })]    public ValueTuple<int, int> Foo(        [TupleElementNames(new[] { "c", "d" })] ValueTuple<int, int> a)    {        return a;    }

这有助于IDE和编译器“检查”元素名称,并警告错误地使用它们:

    // 正确: 元组声明可以跳过元素名称    (int x, int y) tpl = (1, 2);         // 警告: 由于目标类型“(int x, int y)”指定了其他名称或未指定名称,因此元组元素名称“a”被忽略。    tpl = (a:1, b:2);         // 正确 :元组解构忽略元素名称    var (a, b) = tpl;         // x: 2, y: 1. 元组名被忽略    var (y, x) = tpl;

编译器对继承的成员有较强的要求:

    public abstract class Base    {        public abstract (int a, int b) Foo();        public abstract (int, int) Bar();    }         public class Derived : Base    {        // 错误:替代继承成员“Base.Foo()”时无法更改元组元素名称        public override (int c, int d) Foo() => (1, 2);        // 错误:替代继承成员“Base.Bar()”时无法更改元组元素名称        public override (int a, int b) Bar() => (1, 2);    }

常规方法参数可以在重写成员中自由更改,重写成员中的元组元素名称应该与基本类型中的元素名称完全匹配。

元素名称推断

C# 7.1 引入了一个额外的增强功能:元素名称推断类似于C#为匿名类型所做的推断。

    public void NameInference(int x, int y)    {        // (int x, int y)        var tpl = (x, y);             var a = new {X = x, Y = y};             // (int X, int Y)        var tpl2 = (a.X, a.Y);    }

值语义和可变性

元组是公共字段可变的值类型。这听起来令人担忧,因为我们知道可变值类型被认为是有害的。这是一个邪恶的小例子:

    var x = new { Items = new List<int> { 1, 2, 3 }.GetEnumerator() };    while (x.Items.MoveNext())    {        Console.WriteLine(x.Items.Current);    }

如果运行这个代码,您会得到一个无限循环。List<T>.Enumerator是一个可变值类型,但是Items是属性。这意味着x.Items在每个循环迭代中返回原始迭代器的副本,从而导致无限循环。

但是只有当数据与行为混合在一起时,可变值类型才是危险的:枚举器拥有一个状态(当前元素)并具有行为(通过调用MoveNext方法来推进迭代器的能力)。这种组合可能会导致问题,因为在副本上调用方法而不是在原始实例上调用方法,从而导致无效操作。下面是一组由于值类型的隐藏副本而导致不明显行为的示例:gist。

但可变性问题依然存在:

    var tpl = (x: 1, y: 2);    var hs = new HashSet<(int x, int y)>();    hs.Add(tpl);         tpl.x++;    Console.WriteLine(hs.Contains(tpl)); // false

元组在字典中作为键是非常有用的,并且由于适当的值语义可以存储在哈希表中。但是您不应该在集合的不同操作之间改变一个元组变量的状态。

解构

虽然元组的构造函数对于元组来说非常特殊的,但是解构非常通用,并且可以与任何类型一起使用。

    public static class VersionDeconstrucion    {        public static void Deconstruct(this Version v, out int major, out int minor, out int build, out int revision)        {            major = v.Major;            minor = v.Minor;            build = v.Build;            revision = v.Revision;        }    }             var version = Version.Parse("1.2.3.4");    var (major, minor, build, _) = version;         // Prints: 1.2.3    Console.WriteLine($"{major}.{minor}.{build}");

解构使用“鸭子类型(duck-typing)”的方法:如果编译器可以找到一个方法调用Deconstruct给定的类型 - 实例方法或扩展方法 - 类型即是可解构的。

元组别名

一旦您开始使用元组,很快就会意识到想在源代码的多个地方“重用”一个元组类型,但这并没有什么问题。首先,虽然C#不支持给定类型的全局别名,不过您可以使用“using”别名指令,它会在一个文件中创建一个别名;其次,您不能将元组指定别名:

//您不能这样做:编译错误using Point = (int x, int y); // 但是您可以这样做using SetOfPoints = System.Collections.Generic.HashSet<(int x, int y)>;

github上有一个关于“使用指令中的元组类型”的讨论。所以,如果您发现自己在多个地方使用一个元组类型,你有两个选择:保持复制粘贴或创建一个命名的类型。

命名规则

下面是一个有趣的问题:我们应该遵循什么命名规则来处理元组元素?Pascal规则喜欢ElementName还是骆峰规则elementName?一方面,元组元素应该遵循公共成员的命名规则(即PascalCase),但另一方面,元组只是包含变量的变量,变量应该遵循骆峰规则。

如果元组被用作参数或方法的返回类型使用PascalCase规则,并且如果在函数中本地创建元组使用camelCase规则,可以考虑使用基于用法和使用的不同命名方案。但我更喜欢总是使用camelCase

总结

我发现元组在日常工作中非常有用。我需要不止一个函数返回值,或者我需要把一对值放入一个哈希表,或者字典的Key非常复杂,我需要用另一个“字段”来扩展它。

我甚至使用它们来避免与方法类似的ConcurrentDictionary.TryGetOrAdd的闭包分配,需要额外的参数。在许多情况下,状态也是一个元组。

该功能是非常有用的,但我还想看到一些增强功能:

  • 全局别名:能够“命名”一个元组并在整个程序集中使用它们。

  • 在模式匹配中解构一个元组:out varcase var语法。

  • 使用运算符==进行相等比较。

关于“C#中元组类型ValueTuple怎么用”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

C#中元组类型ValueTuple怎么用

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

C#中元组类型ValueTuple怎么用

这篇文章将为大家详细讲解有关C#中元组类型ValueTuple怎么用,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。System.Tuple 类型是在.NET 4.0中引入的,但是有两个明显的缺点:(1)
2023-06-29

C#基元类型、值类型、引用类型是什么

这篇文章主要讲解了“C#基元类型、值类型、引用类型是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C#基元类型、值类型、引用类型是什么”吧!首先了解下什么是基元类型,基元类型是编译器直接
2023-06-17

C#怎么实现数组元素的数据类型转换

这篇文章主要介绍“C#怎么实现数组元素的数据类型转换”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“C#怎么实现数组元素的数据类型转换”文章能帮助大家解决问题。一、场景假设假设有一串字符串如下所示,字
2023-06-30

C#怎么使用System.Buffer以字节数组Byte[]操作基元类型数据

这篇文章主要讲解了“C#怎么使用System.Buffer以字节数组Byte[]操作基元类型数据”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C#怎么使用System.Buffer以字节数组
2023-06-30

C++ 中怎么实现数组类泛型编程

这篇文章给大家介绍C++ 中怎么实现数组类泛型编程,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。原创:C++ 简单实现数组类泛型编程示例1、使用模板来实现泛型编程2、本数组应该能够存储各种基础类型,各种复杂的类类型3、
2023-06-04

Python基本数据类型中元组的用法

本篇文章为大家展示了Python基本数据类型中元组的用法,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。1.元组的概念python中的元组是有序元素组成的集合,与列表的区别在于,元组是不可变的,一旦定
2023-06-02

怎么使用C++中的string类型

这篇文章主要讲解了“怎么使用C++中的string类型”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么使用C++中的string类型”吧!1、string 类1.1 和char *的异同在
2023-06-25

C#中数据类型string怎么用

这篇文章主要为大家展示了“C#中数据类型string怎么用”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“C#中数据类型string怎么用”这篇文章吧。string是各种编程语言中最基础的数据类型
2023-06-17

Golang 中函数类型的组成元素是什么?

go语言函数类型由函数名、输入参数列表、输出参数列表、返回值类型组成。语法:func 函数名(入参列表) (出参列表) 返回值类型;实战:摄氏度转华氏度函数示例:func celsiustofahrenheit(celsius float6
Golang 中函数类型的组成元素是什么?
2024-04-21

C#中的枚举类型怎么使用

在C#中,枚举类型用于定义一组命名的整数常量。以下是枚举类型的基本用法示例:// 定义一个枚举类型public enum DaysOfWeek{Monday,Tuesday,Wednesday,Thursday,Friday,Satu
C#中的枚举类型怎么使用
2024-04-03

C# 中怎么删除数组元素

今天就跟大家聊聊有关C# 中怎么删除数组元素,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。具体如下:using System;using System.Collections.Gen
2023-06-20

C#枚举类型怎么用

这篇文章主要为大家展示了“C#枚举类型怎么用”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“C#枚举类型怎么用”这篇文章吧。C#枚举类型使用的时候需要掌握的有哪些方面呢?首先我们来看看一个例子:p
2023-06-17

C++引用类型怎么使用

这篇文章主要介绍“C++引用类型怎么使用”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“C++引用类型怎么使用”文章能帮助大家解决问题。一、C++中的引用类型思考:如何在被调函数中修改主调函数中定义的
2023-07-02

c++中string类型怎么输出

在 c++ 中输出 string 类型有以下方法:使用 std::cout 对象:std::cout C++ 中输出 string 类型在 C++ 中,您可以使用以下方法来输出 string 类型:std::cout 对象:std::c
c++中string类型怎么输出
2024-05-01

C#枚举类型怎么使用

本篇内容介绍了“C#枚举类型怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!C#枚举,枚举是一种值类型,由许多名字的常量(也叫枚举表)
2023-06-17

c语言word类型怎么用

word 类型是一种 32 位无符号整数类型,用于表示从 0 到 4,294,967,295 范围内的值。它可用于存储地址、计数、标志或位字段,并使用 unsigned int my_word 语法声明。可以通过 i/o 函数访问 word
c语言word类型怎么用
2024-04-13

C#泛型数组怎么使用

本篇内容介绍了“C#泛型数组怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!C# 泛型和数组在 C# 2.0 中,下限为零的一维数组自
2023-06-17

C#中引用类型和值类型有什么区别

在C#中,引用类型和值类型是两种不同的数据类型。值类型是直接存储在堆栈中的数据,它们保存的是实际的数据值。当值类型的变量被赋值给另一个变量时,会复制该变量的值。常见的值类型包括整数(int)、浮点数(float)、布尔值(bool)等。
C#中引用类型和值类型有什么区别
2024-03-07

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录