元组是一种在轻量级数据结构中将多个数据元素分组的数据结构。元组可以作为一种简便的方法来表示多个数据元素,将它们作为参数传递或作为返回值接收,而无需声明一个完整的新类或结构来保存数据集。
例如,下面的代码使用通用的 System.Collections.Generic.Tuple 类型从函数中接收多个值,而无需声明多个 REF/OUT 参数或创建一个新的专用类型来保存它们:
USING System.Collections.Generic
FUNCTION GetCustomerData() AS Tuple<STRING, INT, LOGIC>
LOCAL oCustomer AS Tuple<STRING, INT, LOGIC>
oCustomer := Tuple<STRING, INT, LOGIC>{"Nikos", 47, TRUE}
RETURN oCustomer
FUNCTION Start() AS VOID
LOCAL oCustomer AS Tuple<STRING, INT, LOGIC>
oCustomer := GetCustomerData()
? "Customer name:", oCustomer:Item1
? "Age:", oCustomer:Item2
? "Is active:", oCustomer:Item3
X# 还支持专用关键字 TUPLE,它为元组的声明和处理提供了更简便的语法。使用 TUPLE 关键字语法可以更简单地编写上述代码:
FUNCTION GetCustomerData() AS TUPLE(STRING, INT, LOGIC)
LOCAL oCustomer AS TUPLE(STRING, INT, LOGIC) // 定义元组
// 元组项目类型必须与元组定义中的类型一致
oCustomer := TUPLE{"Nikos", 47, TRUE}
RETURN oCustomer
FUNCTION Start() AS VOID
LOCAL oCustomer AS (STRING, INT, LOGIC)
// 甚至可以省略元组定义中的 TUPLE 关键字!
oCustomer := GetCustomerData()
? "Customer name:", oCustomer:Item1
? "Age:", oCustomer:Item2
? "Is active:", oCustomer:Item3
请注意,使用 TUPLE 关键字时,编译器会在内部使用 System.ValueTuple 类型,而不是通用类型 System.Collections.Generic.Tuple。此外,在定义元组时,可以省略 TUPLE 关键字,从而使用更类似于 c# 的语法(type1、type2、...)
专用的 TUPLE 关键字语法还支持为元组项指定自定义名称,而不是使用 Item1、Item2 等通用名称。这样,代码的可读性就会大大提高,并接近于使用单独的新类来处理数据时的效果:
FUNCTION Start() AS VOID
LOCAL oCustomer AS TUPLE(Name AS STRING, Age AS INT, IsActive AS LOGIC)
// 为元组中的每个项目提供名称
oCustomer := GetCustomerData()
? "Customer name:", oCustomer:Name
? "Age:", oCustomer:Age
? "Is active:", oCustomer:IsActive
为了使代码更加一目了然,命名元组也可以通过使用命名参数来实例化:
FUNCTION GetCustomerData() AS Tuple(STRING, INT, LOGIC)
// 用自定义项目名称定义命名元组
LOCAL oCustomer AS Tuple(Name AS STRING, Age AS INT, IsActive AS LOGIC)
// 在元组实例化中包括元组项目名称
oCustomer := TUPLE{Name := "Nikos", Age := 47, IsActive := TRUE}
RETURN oCustomer
使用 VAR 关键字还可以同时定义元组和实例化元组:
FUNCTION Start() AS VOID
VAR oTuple := TUPLE{"Nikos", 47, FALSE}
? oTuple:Item1, oTuple:Item2, oTuple:Item3 // "Nikos", 47, FALSE
在这种情况下,每个元组项的数据类型是根据代码中提供的项类型推断出来的。在上述示例中,项目类型为(STRING、INT、LOGIC)。
具有相同项目类型的元组可以互相赋值(从而将项目值从源元组复制到目标元组),对于使用 LOCAL 或 VAR 定义的元组也是如此:
FUNCTION Start() AS VOID
VAR oTuple := TUPLE{"Nikos", 47, FALSE}
LOCAL oNew AS TUPLE(STRING, INT, LOGIC)
oNew := oTuple
? oNew:Item1, oNew:Item2, oNew:Item3 // "Nikos", 47, FALSE
使用 VAR 关键字定义元组时,还可以提供项目名称:
FUNCTION Start() AS VOID
VAR oTuple := TUPLE{Name := "Nikos", Age := 47, IsActive := FALSE}
? oTuple:Name, oTuple:Age, oTuple:IsActive // "Nikos", 47, FALSE
最后,在使用 VAR 定义元组时,如果使用标识符来定义项值,而不是使用字面值,那么每个项都会自动根据标识符名称获得一个名称:
CLASS CustomerInfo
EXPORT Description := "Customer description" AS STRING
END CLASS
FUNCTION Start() AS VOID
LOCAL name := "Unknown Customer" AS STRING
LOCAL oInfo := CustomerInfo{} AS CustomerInfo
VAR oTuple := TUPLE{name, oInfo:Description}
? oTuple:name // "未知客户"
? oTuple:description // "客户描述"
使用 (var1, var2, ...)语法,可以在一行代码中将元组分解为多个普通变量:
FUNCTION Start() AS VOID
LOCAL oCustomer AS TUPLE(Name AS STRING, Age AS INT)
oCustomer := TUPLE{"Nikos", 47}
LOCAL name AS STRING
LOCAL age AS INT
(name, age) := oCustomer
? name, age // "Nikos", 47
也可以使用针对元组的特殊 LOCAL 语法,在一行中定义局部变量并将其赋值给元组项值:
FUNCTION Start() AS VOID
LOCAL oCustomer AS TUPLE(Name AS STRING, Age AS INT)
oCustomer := TUPLE{"Nikos", 47}
LOCAL (name AS STRING, age AS INT) := oCustomer
? name, age // "Nikos", 47
// 您也可以在不使用 LOCAL 关键字的情况下析构到现有的局部变量中:
(name, age) := oCustomer
此外,VAR 关键字也可用于元组析构,在这种情况下,变量类型是从元组项类型中推断出来的:
FUNCTION Start() AS VOID
LOCAL oCustomer AS TUPLE(Name AS STRING, Age AS INT)
oCustomer := TUPLE{"Nikos", 47}
VAR (name, age) := oCustomer
? name, age // "Nikos", 47