C#教程 - 元组与解构(Tuples and Deconstruction )

发布时间 2023-10-10 10:36:47作者: 漫思

C#教程 - 元组与解构(Tuples and Deconstruction )

 

更新记录
转载请注明出处:
2022年9月24日 发布。
2022年9月10日 从笔记迁移到博客。

元组(tuples)说明#

注意:C# 7.0可用
注意:元组不可以声明为静态类型
作用:元组常用于传递和返回多个值;匿名类型可以做的,Tuples基本都可以完成
元组是可变的(mutable),可以直接修改元组的成员
需要System. ValueType<T,T…>泛型类型支持
这个类型并非.NET Framework 4.6的一部分,4.7版本包含这个类型
注意:元组(Tuple)是值类型(value types),是可变类型(mutable (read/write))
tuple可以不定义元素名称,也可以定义命名Tuple元素(Naming Tuple Elements)

实例:

var bob = ("Bob", 23);
var joe = bob;                     // joe is a *copy* of bob
joe.Item1 = "Joe";                 // Change joe's Item1 from Bob to Joe
Console.WriteLine (bob);           // (Bob, 23)
Console.WriteLine (joe);           // (Joe, 23)

定义元组#

注意:命名元素后,仍然可以使用Item1这样的方式访问元素

注意:如果定义时,使用了外部的变量,可以直接使用.外部变量名方式访问

注意:tuple之间可以相互赋值,只要类型之间是兼容的

显式定义#

实例:定义Tuple

var bob = ("Bob", 23); // Allow compiler to infer the element types
Console.WriteLine (bob.Item1); // Bob
Console.WriteLine (bob.Item2); // 23

实例:

//显式定义
(string name, int age, long code) panda1 = ("Panda1", 18, 666);
Console.WriteLine(panda1.name);
Console.WriteLine(panda1.age);
Console.WriteLine(panda1.code);

隐式定义#

//隐式定义1
var panda2 = ("Panda2", 18, 888);
Console.WriteLine(panda2.Item1);
Console.WriteLine(panda2.Item2);
Console.WriteLine(panda2.Item3);
//隐式定义2(Naming Tuple Elements)
var panda3 = (name: "Panda3", age: 18, code: 666);
Console.WriteLine(panda3.name);
Console.WriteLine(panda3.age);
Console.WriteLine(panda3.code);

更多实例:
实例:

var tuple = (name:"Bob", age:23);
Console.WriteLine (tuple.name);     // Bob
Console.WriteLine (tuple.age);      // 23

实例:

static (string name, int age) GetPerson() => ("Bob", 23);

实例:

(string name, int age, char sex)  bob1 = ("Bob", 23, 'M');

实例:定义Tuple(显式类型)

(string,int) bob = ("Bob", 23);

实例:定义tuple带元素命名

var tuple = (name:"Bob", age:23);
Console.WriteLine (tuple.name); // Bob
Console.WriteLine (tuple.age); // 23

实例:Tuple是值类型测试

var bob = ("Bob", 23); // Allow compiler to infer the element types
Console.WriteLine (bob.Item1); // Bob
Console.WriteLine (bob.Item2); // 23
var joe = bob; // joe is a *copy* of bob
joe.Item1 = "Joe"; // Change joe's Item1 from Bob to Joe
Console.WriteLine (bob); // (Bob, 23)
Console.WriteLine (joe); // (Joe, 23)

实例:直接使用外部变量名来访问

var now = DateTime.Now;
var tuple = (now.Day, now.Month, now.Year);
Console.WriteLine (tuple.Day); // OK

实例:方法返回tuple类型

static (string,int) GetPerson() => ("Bob", 23);

实例:方法返回tuple类型带命名

static (string name, int age) GetPerson() => ("Bob", 23);
var person = GetPerson();
Console.WriteLine (person.name); // Bob
Console.WriteLine (person.age); // 23

实例:tuple用在泛型类型中

Task<(string,int)>
Dictionary<(string,int),Uri>
IEnumerable<(int id, string name)> // See below for naming elements

实例:类型兼容的赋值

(string name, int age, char sex) bob1 = ("Bob", 23, 'M');
(string age, int sex, char name) bob2 = bob1; // No error!

元组作为返回值#

简单元组返回值

using System;
using System.Runtime.Versioning;

namespace ConsoleApp1
{
    class PandaClass
    {
        //实例
        public (string,int) DoSomething()
        {
            return ("Panda", 666);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            PandaClass pandaClass = new PandaClass();
            //测试
            (string,int) result = pandaClass.DoSomething();
            Console.WriteLine(result.Item1); //Panda
            Console.WriteLine(result.Item2); //666

            //wait
            Console.ReadKey();
        }
    }
}

元组返回值 与 泛型 结合使用

using System;
using System.Collections.Generic;
using System.Runtime.Versioning;

namespace ConsoleApp1
{
    class PandaClass
    {
        //实例
        public List<(string, int)> DoSomething()
        {
            return new List<(string, int)> { ("Panda", 666) };
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            PandaClass pandaClass = new PandaClass();
            //测试
            List<(string, int)> result = pandaClass.DoSomething();
            Console.WriteLine(result.Count); //1
            Console.WriteLine(result[0].Item1); //panda
            Console.WriteLine(result[0].Item2); //666

            //wait
            Console.ReadKey();
        }
    }
}

元组返回值 与 成员命令

using System;
using System.Collections.Generic;
using System.Runtime.Versioning;

namespace ConsoleApp1
{
    class PandaClass
    {
        //实例
        public List<(string website, int code)> DoSomething()
        {
            return new List<(string, int)> { ("Panda", 666) };
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            PandaClass pandaClass = new PandaClass();
            //测试
            List<(string website, int code)> result = pandaClass.DoSomething();
            Console.WriteLine(result.Count); //1
            Console.WriteLine(result[0].website); //panda
            Console.WriteLine(result[0].code); //666

            //wait
            Console.ReadKey();
        }
    }
}

元组解构(Deconstructing Tuples)#

Tuples implicitly support the deconstruction pattern

//显式解构
(string name, int age, int code) = ("Panda", 18, 666);
Console.WriteLine(name);
Console.WriteLine(age);
Console.WriteLine(code);
//隐式解构
(var name, var age, var code) = ("Panda", 18, 666);
Console.WriteLine(name);
Console.WriteLine(age);
Console.WriteLine(code);
//使用现有变量
string name;
int age;
int code;
(name, age, code) = ("Panda", 18, 666);
//省略不需要的数据(使用下划线代替即可)
(string name, _, int code) = ("Panda666", 18, 666);
Console.WriteLine(name);
Console.WriteLine(code);

实例:

var bob = ("Bob", 23);
(string name, int age) = bob;
Console.WriteLine (name);
Console.WriteLine (age);

实例:从方法返回值中解构

static (string, int, char) GetBob() => ( "Bob", 23, 'M');
static void Main()
{
    var (name, age, sex) = GetBob();
    Console.WriteLine (name); // Bob
    Console.WriteLine (age); // 23
    Console.WriteLine (sex); // M
}

元组的本质#

匿名类型会在底层定义自定义类型

元组在底层使用预定义的System.ValueTuple<T,T…>泛型结构

public struct ValueTuple<T1>

public struct ValueTuple<T1,T2>

public struct ValueTuple<T1,T2,T3>

//...

每种ValueTuple都会定义Item1,Item2,...这样的属性

所以 (string,int) 是 ValueTuple<string,int>的别名

命名参数的Tuplue会在编译的时候,将自定义命名转为Item1,...这样的属性

比如:(string,int)在后台被转为 ValueTuple<string,int>

ValueTuple<string, int, int> s = ValueTuple.Create("Panda666", 18, 666);
Console.WriteLine(s.Item1);
Console.WriteLine(s.Item2);
Console.WriteLine(s.Item3);

有个例外是在方法返回命名Tuple时,会保留命名属性

底层使用TupleElementNamesAttribute特性来实现的

ValueTuple.Create#

除了定义式声明Tuple,还可以使用Create工厂方法来创建Tuple

注意:这种方式无法定义命名的Tuple。因为命名Tuple是编译器实现的

实例:

ValueTuple<string,int> bob1 = ValueTuple.Create ("Bob", 23);
(string,int) bob2 = ValueTuple.Create ("Bob", 23);

元组的比较(Tuple Comparison)#

ValueTuple在底层重写了Equal方法,可以直接使用,只要是成员顺序和值相同

ValueTuple<> overloads the == and != operators,从C# 7.3开始

元组还实现了IComparable接口,所以可以作为排序的键

ValueTuple<> also implement IComparable

实例:

using System;
using System.Collections.Generic;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            var panda1 = ("Panda666.com",666);
            var panda2 = ("Panda666.com",666);
            Console.WriteLine(panda1 == panda2);      //true
            Console.WriteLine(panda1.Equals(panda2)); //true
            
            //wait
            Console.ReadKey();
        }
    }
}

实例:

var t1 = ("one", 1);
var t2 = ("one", 1);
Console.WriteLine (t1 == t2);    // True (from C# 7.3)
Console.WriteLine (t1.Equals (t2));    // True

实例:使用Equals方法

var t1 = ("one", 1);
var t2 = ("one", 1);
Console.WriteLine (t1.Equals (t2)); // True
Console.WriteLine (t1 == t2); // 使用==运算符,True

元组与System.Tuple类型#

Tuple在.NET Framework 4.0中引入

System.Tuple是一个类,而System.ValueType是一个泛型结构

Tuple没有C#语言支持,而元组有

优先考虑使用ValueTuple

实例:

Tuple<string,int> t = Tuple.Create ("Bob", 23); // Factory method
Console.WriteLine (t.Item1); // Bob
Console.WriteLine (t.Item2); // 23

Tuple实例:

using System;
using System.Collections.Generic;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Tuple<string, int> tuple1 = new Tuple<string, int>("Panda", 666);
            Tuple<string, int> tuple2 = Tuple.Create("Panda", 666);
            Console.WriteLine(tuple1.Item1); //Panda
            Console.WriteLine(tuple1.Item2); //666
            Console.WriteLine(tuple2.Item1); //Panda
            Console.WriteLine(tuple2.Item2); //666

            //wait
            Console.ReadKey();
        }
    }
}

作者:Panda666