大家都知道新特性里面有个对元组的优化,  

作者: 编程  发布:2019-11-04

 元组Tuple

  我们在开发的时候,都会常常遇到一个问题,如何为逻辑上返回多个对象设计方法签名。通常我们会想到使用out或ref修饰参数,或者麻烦些自己设定一个类型内部包含多个Object类型属性来接收多个返回值。这两中方式都不够好,前者让程序难以实现多态(out或ref修饰的参数类型,不能使用派生类替代),设计走向是面向过程,也难以使用链式编程;后者因为所有的返回都Object类型,所以在使用时都要进行转换成真实的类型。在这种情况下我们可以使用泛型元组类型来处。在.Net 4.0中新增了Tuple与Tuple<T1>、Tuple<T1,T2>、Tuple<T1,......,T7,TRest>。就解决了上面的问题。

详解C# Tuple VS ValueTuple(元组类 VS 值元组),

C# 7.0已经出来一段时间了,大家都知道新特性里面有个对元组的优化:ValueTuple。这里利用详尽的例子详解Tuple VS ValueTuple(元组类VS值元组),10分钟让你更了解ValueTuple的好处和用法。

如果您对Tuple足够了解,可以直接跳过章节”回顾Tuple”,直达章节”ValueTuple详解”,查看值元组的炫丽用法。

 

 

  其泛型元组可以更好的处理4种情况:

回顾Tuple

Tuple是C# 4.0时出的新特性,.Net Framework 4.0以上版本可用。

元组是一种数据结构,具有特定数量和元素序列。比如设计一个三元组数据结构用于存储学生信息,一共包含三个元素,第一个是名字,第二个是年龄,第三个是身高。

元组的具体使用如下:

  我们现在使用的C#语法已经可以满足日常的开发需求,但C#语法还在进行版本的更新,在创造更多更优秀的语义来让我们使用。这里介绍一下C#5.0里的提供的语法——元组。

  ①替代out与ref修饰的参数(即方法含有多个返回值)。

1.    如何创建元组

默认情况.Net Framework元组仅支持1到7个元组元素,如果有8个元素或者更多,需要使用Tuple的嵌套和Rest属性去实现。另外Tuple类提供创造元组对象的静态方法。

  • 利用构造函数创建元组:
var testTuple6 = new Tuple<int, int, int, int, int, int>(1, 2, 3, 4, 5, 6);
Console.WriteLine($"Item 1: {testTuple6.Item1}, Item 6: {testTuple6.Item6}");

var testTuple10 = new Tuple<int, int, int, int, int, int, int, Tuple<int, int, int>>(1, 2, 3, 4, 5, 6, 7, new Tuple<int, int, int>(8, 9, 10));
Console.WriteLine($"Item 1: {testTuple10.Item1}, Item 10: {testTuple10.Rest.Item3}");
  • 利用Tuple静态方法构建元组,最多支持八个元素:
var testTuple6 = Tuple.Create<int, int, int, int, int, int>(1, 2, 3, 4, 5, 6);
Console.WriteLine($"Item 1: {testTuple6.Item1}, Item 6: {testTuple6.Item6}");

var testTuple8 = Tuple.Create<int, int, int, int, int, int, int, int>(1, 2, 3, 4, 5, 6, 7, 8);
Console.WriteLine($"Item 1: {testTuple8.Item1}, Item 8: {testTuple8.Rest.Item1}");

Note:这里构建出来的Tuple类型其实是Tuple<int, int, int, int, int, int, int, Tuple<int>>,因此testTuple8.Rest取到的数据类型是Tuple<int>,因此要想获取准确值需要取Item1属性。

  在C#中定义Tuple对象,转到定义查看,我们会看到如下代码

  ②通过单个参数将多个值传递给一个方法。例如, Thread.Start(Object) 方法有一个单一参数,可以使用该参数向在启动时线程执行的方法提供一个值。

2.    表示一组数据

如下创建一个元组表示一个学生的三个信息:名字、年龄和身高,而不用单独额外创建一个类。

var studentInfo = Tuple.Create<string, int, uint>("Bob", 28, 175);
Console.WriteLine($"Student Information: Name [{studentInfo.Item1}], Age [{studentInfo.Item2}], Height [{studentInfo.Item3}]");
 #region 程序集 mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
 // C:Program Files (x86)Reference AssembliesMicrosoftFramework.NETFrameworkv4.6mscorlib.dll
 #endregion

  ③提供对数据集的轻松访问和操作。如一条数据(Tuple)中含有多个子数据(Tuple中属性),多条记录合并成了数据集(类型为Tuple的集合)。

3.    从方法返回多个值

当一个函数需要返回多个值的时候,一般情况下可以使用out参数,这里可以用元组代替out实现返回多个值。

static Tuple<string, int, uint> GetStudentInfo(string name)
{
    return new Tuple<string, int, uint>("Bob", 28, 175);
}

static void RunTest()
{
    var studentInfo = GetStudentInfo("Bob");
    Console.WriteLine($"Student Information: Name [{studentInfo.Item1}], Age [{studentInfo.Item2}], Height [{studentInfo.Item3}]");
}

4.    用于单参数方法的多值传递

当函数参数仅是一个Object类型时,可以使用元组实现传递多个参数值。

static void WriteStudentInfo(Object student)
{
    var studentInfo = student as Tuple<string, int, uint>;
    Console.WriteLine($"Student Information: Name [{studentInfo.Item1}], Age [{studentInfo.Item2}], Height [{studentInfo.Item3}]");
}

static void RunTest()
{
    var t = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(WriteStudentInfo));
    t.Start(new Tuple<string, int, uint>("Bob", 28, 175));
    while (t.IsAlive)
    {
        System.Threading.Thread.Sleep(50);
    }
}

 

尽管元组有上述方便使用的方法,但是它也有明显的不足:

  • 访问元素的时候只能通过ItemX去访问,使用前需要明确元素顺序,属性名字没有实际意义,不方便记忆;
  • 最多有八个元素,要想更多只能通过最后一个元素进行嵌套扩展;
  • Tuple是一个引用类型,不像其它的简单类型一样是值类型,它在堆上分配空间,在CPU密集操作时可能有太多的创建和分配工作。

因此在C# 7.0中引入了一个新的ValueTuple类型,详见下面章节。

  即该语法在.Net Framework4框架中已经可以支持了。

  ④表示一组数据。例如,元组可以表示数据库中的一条记录,并且其属性可以表示该记录的字段。

ValueTuple详解

ValueTuple是C# 7.0的新特性之一,.Net Framework 4.7以上版本可用。

值元组也是一种数据结构,用于表示特定数量和元素序列,但是是和元组类不一样的,主要区别如下:

  • 值元组是结构,是值类型,不是类,而元组(Tuple)是类,引用类型;
  • 值元组元素是可变的,不是只读的,也就是说可以改变值元组中的元素值;
  • 值元组的数据成员是字段不是属性。

值元组的具体使用如下:

  元组Tuple是一种数据结构,具有特定数量和元素序列。什么意思呢?就是元组是可以存贮多种类型的对象,可以想象一下当一个函数拥有多个不同类型的返回值时,我们除了定义了一个返回值以外,还要定义多个out或ref类型返回值才能解决这个需求;当然我们也可以定义一个对象保存多个返回值。但现在我们多了一个解决方案,定义返回值为一个元组,就解决了一切。

  如下:展示了一个泛型Tuple的声明

1.    如何创建值元组

和元组类一样,.Net Framework值元组也只支持1到7个元组元素,如果有8个元素或者更多,需要使用值元组的嵌套和Rest属性去实现。另外ValueTuple类可以提供创造值元组对象的静态方法。

  • 利用构造函数创建元组:
var testTuple6 = new ValueTuple<int, int, int, int, int, int>(1, 2, 3, 4, 5, 6);
Console.WriteLine($"Item 1: {testTuple6.Item1}, Item 6: {testTuple6.Item6}"); 

var testTuple10 = new ValueTuple<int, int, int, int, int, int, int, ValueTuple<int, int, int>>(1, 2, 3, 4, 5, 6, 7, new ValueTuple <int, int, int>(8, 9, 10));
Console.WriteLine($"Item 1: {testTuple10.Item1}, Item 10: {testTuple10.Rest.Item3}");
  • 利用Tuple静态方法构建元组,最多支持八个元素:
var testTuple6 = ValueTuple.Create<int, int, int, int, int, int>(1, 2, 3, 4, 5, 6);
Console.WriteLine($"Item 1: {testTuple6.Item1}, Item 6: {testTuple6.Item6}"); 

var testTuple8 = ValueTuple.Create<int, int, int, int, int, int, int, int>(1, 2, 3, 4, 5, 6, 7, 8);
Console.WriteLine($"Item 1: {testTuple8.Item1}, Item 8: {testTuple8.Rest.Item1}");

注意这里构建出来的Tuple类型其实是Tuple<int, int, int, int, int, int, int, Tuple<int>>,因此testTuple8.Rest取到的数据类型是Tuple<int>,因此要想获取准确值需要取Item1属性。

优化区别:当构造出超过7个元素以上的值元组后,可以使用接下来的ItemX进行访问嵌套元组中的值,对于上面的例子,要访问第十个元素,既可以通过testTuple10.Rest.Item3访问,也可以通过testTuple10.Item10来访问。

var testTuple10 = new ValueTuple<int, int, int, int, int, int, int, ValueTuple<int, int, int>>(1, 2, 3, 4, 5, 6, 7, new ValueTuple<int, int, int>(8, 9, 10));
Console.WriteLine($"Item 10: {testTuple10.Rest.Item3}, Item 10: {testTuple10.Item10}");

  元组Tuple是可以存贮多种类型的数据的。NET Framework 直接支持具有 1 到 7 元素的元组。 此外,您可以创建由嵌套中的元组对象的元组的八个或多个元素Rest属性Tuple<T1, T2, T3, T4, T5, T6, T7, TRest>对象。

 public class Tuple<T1, T2, T3> : IStructuralEquatable, IStructuralComparable, IComparable
    {
        public Tuple(T1 item1, T2 item2, T3 item3);
        public T1 Item1 { get; }
        public T2 Item2 { get; }
        public T3 Item3 { get; }
    //省略方法的具体实现
        public override bool Equals(object obj);
        public override int GetHashCode();
        public override string ToString();
    //省略对借口的实现
    }

2.    表示一组数据

如下创建一个值元组表示一个学生的三个信息:名字、年龄和身高,而不用单独额外创建一个类。

var studentInfo = ValueTuple.Create<string, int, uint>("Bob", 28, 175);
Console.WriteLine($"Student Information: Name [{studentInfo.Item1}], Age [{studentInfo.Item2}], Height [{studentInfo.Item3}]");

  元组常用四种方法︰

  在.Net4.0以下的版本虽然没有框架自带的泛型元组,但是我们可以自己定义泛型元组(代码结构如上),其类型中的比较与相等的接口也可以根据需要是否实现。

3.    从方法返回多个值

值元组也可以在函数定义中代替out参数返回多个值。

static ValueTuple<string, int, uint> GetStudentInfo(string name)
{
    return new ValueTuple <string, int, uint>("Bob", 28, 175);
}

static void RunTest()
{
    var studentInfo = GetStudentInfo("Bob");
    Console.WriteLine($"Student Information: Name [{studentInfo.Item1}], Age [{studentInfo.Item2}], Height [{studentInfo.Item3}]");
}

优化区别:返回值可以不明显指定ValueTuple,使用新语法(,,)代替,如(string, int, uint):

static (string, int, uint) GetStudentInfo1(string name)
{
    return ("Bob", 28, 175);
}

static void RunTest1()
{
    var studentInfo = GetStudentInfo1("Bob");
    Console.WriteLine($"Student Information: Name [{studentInfo.Item1}], Age [{studentInfo.Item2}], Height [{studentInfo.Item3}]");
}

调试查看studentInfo的类型就是ValueType三元组。

优化区别:返回值可以指定元素名字,方便理解记忆赋值和访问:

static (string name, int age, uint height) GetStudentInfo1(string name)
{
    return ("Bob", 28, 175);
}

static void RunTest1()
{
    var studentInfo = GetStudentInfo1("Bob");
    Console.WriteLine($"Student Information: Name [{studentInfo.name}], Age [{studentInfo.age}], Height [{studentInfo.height}]");
}

方便记忆赋值:

图片 1

方便访问:

图片 2

  1,用来表示一组数据。 例如,一个元组可以表示的数据库记录,并且其组件可以表示每个字段的记录。

如下分别为out或ref修饰参数使用、泛型元组Tuple<T1,T2>的使用

4.    用于单参数方法的多值传递

当函数参数仅是一个Object类型时,可以使用值元组实现传递多个值。

static void WriteStudentInfo(Object student)
{
    var studentInfo = (ValueTuple<string, int, uint>)student;
    Console.WriteLine($"Student Information: Name [{studentInfo.Item1}], Age [{studentInfo.Item2}], Height [{studentInfo.Item3}]");
}

static void RunTest()
{
    var t = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(WriteStudentInfo));
    t.Start(new ValueTuple<string, int, uint>("Bob", 28, 175));
    while (t.IsAlive)
    {
        System.Threading.Thread.Sleep(50);
    }
}

  2,若要提供轻松访问和数据集的操作。

public static float TemperatureOfCity(string cityId, out string cityName)
        {
            //省略数据处理内容
            cityName = "腾冲";
            float temperature = 23.6f;
            return temperature;
        }
public static Tuple<string, float> TemperatureOfCity(string cityId)
        {
            //省略数据处理内容
            string cityName = "腾冲";
            float temperature = 23.6f;
            return new Tuple<string, float>(cityName, temperature);
        }

5.    解构ValueTuple

可以通过var (x, y)或者(var x, var y)来解析值元组元素构造局部变量,同时可以使用符号”_”来忽略不需要的元素。

static (string name, int age, uint height) GetStudentInfo1(string name)
{
    return ("Bob", 28, 175);
}

static void RunTest1()
{
    var (name, age, height) = GetStudentInfo1("Bob");
    Console.WriteLine($"Student Information: Name [{name}], Age [{age}], Height [{height}]");

    (var name1, var age1, var height1) = GetStudentInfo1("Bob");
    Console.WriteLine($"Student Information: Name [{name1}], Age [{age1}], Height [{height1}]");

    var (_, age2, _) = GetStudentInfo1("Bob");
    Console.WriteLine($"Student Information: Age [{age2}]");
}

 

由上所述,ValueTuple使C#变得更简单易用。较Tuple相比主要好处如下:

  • ValueTuple支持函数返回值新语法”(,,)”,使代码更简单;
  • 能够给元素命名,方便使用和记忆,这里需要注意虽然命名了,但是实际上value tuple没有定义这样名字的属性或者字段,真正的名字仍然是ItemX,所有的元素名字都只是设计和编译时用的,不是运行时用的(因此注意对该类型的序列化和反序列化操作);
  • 可以使用解构方法更方便地使用部分或全部元组的元素;
  • 值元组是值类型,使用起来比引用类型的元组效率高,并且值元组是有比较方法的,可以用于比较是否相等,详见:

 

[原创文章,转载请注明出处,仅供学习研究之用,如有错误请留言,如觉得不错请推荐,谢谢支持]

[原文:]

Tuple VS ValueTuple(元组类 VS 值元组), C# 7.0已经出来一段时间了,大家都知道新特性里面有个对元组的优化:ValueTuple。这里利用详尽...

  3,out参数 (在 C# 中) 或ByRef参数 (在 Visual Basic 中)。

  可能你并不希望此类Tuple的定义扰乱了类型的真正含义,好在C#的设计者也考略到这个问题,允许我们使用using语句为任意的封闭泛型类型声明一个别名。

  4,若要将多个值传递给通过单个参数的方法。 例如,Thread.Start(Object)方法只有一个参数,允许你提供一个线程在启动时执行的方法的值。如果你提供Tuple<T1, T2, T3>对象作为方法自变量,则可以提供有三个项的数据的线程的启动例程。

using CityTemperature = Tuple<string, float>;
public static Tuple<string, float> TemperatureOfCity(string cityId)
        {
        //省略数据处理内容
            string cityName = "腾冲";
            float temperature = 23.6f;
            return new CityTemperature(cityName, temperature);
        }
 class Program
    {
        static void Main(string[] args)
        {
            var tuple = new Tuple<string, int, int, int>(
                             "Kiba", 00001, 00002,
                             00003);

            Console.WriteLine(tuple.Item1);
            Console.WriteLine(tuple.Item2);
            Console.WriteLine(tuple.Item3);
            Console.WriteLine(tuple.Item4);

            var tupleCalss = new Tuple<A, B>(
                         new A(), new B());
            Console.WriteLine(tupleCalss.Item1.Name);
            Console.WriteLine(tupleCalss.Item2.Name);
            Console.ReadKey();
        }
    }
    public class A
    {
        public string name = "A";

        public string Name { get => name; set => name = value; }
    }
    public class B
    {
        public string Name = "B";
    }
}

  以上虽然只详细的列出了泛型元组处理的4中情况的(替代out或ref参数)一种,但是其他3中情况的代码编写都是类似。就不一一明细列出。

输出结果
Kiba
1
2
3
A
B


 

【PS:这里使用的目标框架是.net framework 4.0 ,我们可以看到属性的声明如下,即4.0已经支持=>模式的属性设置了。】

 public string name = "A";

 public string Name { get => name; set => name = value; }

C#语法——委托,架构的血液

C#语法——泛型的多种应用

C#语法——await与async的正确打开方式


注:此文章为原创,欢迎转载,请在文章页面明显位置给出此文链接!
若您觉得这篇文章还不错,请点击下右下角的推荐,非常感谢!

本文由9159.com发布于编程,转载请注明出处:大家都知道新特性里面有个对元组的优化,  

关键词:

上一篇:没有了
下一篇:没有了