C# 元组和值元组的具体使用

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

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

回顾tuple

tuple是c# 4.0时出的新特性,.net framework 4.0以上版本可用。

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

元组的具体使用如下:

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属性。

2.    表示一组数据

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

var studentinfo = tuple.create<string, int, uint>("bob", 28, 175);
console.writeline($"student information: name [{studentinfo.item1}], age [{studentinfo.item2}], height [{studentinfo.item3}]");

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类型,详见下面章节。

valuetuple详解

valuetuple是c# 7.0的新特性之一,.net framework 4.7以上版本可用。

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

  • 值元组是结构,是值类型,不是类,而元组(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}");

2.    表示一组数据

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

var studentinfo = valuetuple.create<string, int, uint>("bob", 28, 175);
console.writeline($"student information: name [{studentinfo.item1}], age [{studentinfo.item2}], height [{studentinfo.item3}]");

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}]");
}

方便记忆赋值:

 

方便访问:

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);
  }
}

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,所有的元素名字都只是设计和编译时用的,不是运行时用的(因此注意对该类型的序列化和反序列化操作);
  • 可以使用解构方法更方便地使用部分或全部元组的元素;
  • 值元组是值类型,使用起来比引用类型的元组效率高,并且值元组是有比较方法的,可以用于比较是否相等,详见:。

到此这篇关于c# 元组和值元组的具体使用的文章就介绍到这了,更多相关c# 元组和值元组内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!

(0)
上一篇 2022年3月23日
下一篇 2022年3月23日

相关推荐