likes
comments
collection
share

Dart系列 --- 记录类型 (元组)

作者站长头像
站长
· 阅读数 1

记录类型 (元组)是 Dart 3 中引入的新功能。

您还需要在 pubspec.yaml 文件中将 Dart 3 设置为 sdk:sdk: ">=3.0.0 <4.0.0"

前言

在 dart 2 中,当您想要将多个对象捆绑到一个值中多个对象时,您有 2 个选择。

1:创建一个包含对象作为属性的类

class DataClass {  
  final int number;  
  final String string; 
  final double float;   
  const DataClass(this.number, this.string, this.float);
}

如果对象过于太多的话,这个策略就会显得很冗长

2:使用集合,例如ListMap ,或Set

List<Object> collection = [0, 'a', 0.0];

问题是该选项不是类型安全的:

ListMap 和 Set 可以存储单一类型(List<int>List<String> 或 List<double>)。 如果您需要多种类型,您通常会回退到 List<Object>

记录类型 (元组) 在dart3的使用

(int, String, double) record = (0, 'a', 0.0); 
// Or without type annotationfinal 
record = (0, 'a', 0.0);

上面的代码创建了一条包含 intString 和 double 的元组,无需创建类或使用集合。

定义一个元组看起来与定义一个List完全相同,但用 () 而不是 []

然后,您可以通过索引访问每个值(从 1 开始),前缀为 $

(int, String, double) record =  (0, 'a', 0.0);
int number = record.$1;
String string = record.$2;
double float = record.$3;

您还可以为元组中的每个值指定一个名称

({double float, int number, String string}) 
record = (number: 1, string: 'a', float: 2.0); 
// Or without type annotationfinal
record = (number: 1, string: 'a', float: 2.0);

通过这样做,元组就像没有名称的类一样:您可以从其名称访问每个字段。

{double float, int number, String string}) record = (number: 1, string: 'a', float: 2.0); 
int number = record.number;
String string = record.string
double float = record.float;

还可以使用命名字段和未命名字段的组合:

(int, double, {String string}) record = (1, string: 'a', 2.0);

此外,您还可以在一行中解构所有元组:

final (number, string, float) = record;

使用元组

我们使用新的元组类型将 Card 构造为 Suit 和 Rank 的组合:

typedef Card = (Suit, Rank);

这个新的 () 语法定义了一个 Card(在本例中为 Tuple,因为它由 2 种类型组成)。 在 dart 3.0 之前,这是不可能的

注意typedef允许为类型指定另一个名称,以后每次使用Card而无需继续写(Suit, Rank) ,使得代码更加清晰。

这样可以避免为简单类型创建类。在 dart 3.0 之前,相同的结果需要 class:

/// Before dart 3.0
class Card { 
final Suit suit; 
final Rank rank;  
const Card(this.suit, this.rank);
}

使用这个新模型(sealed 和 typedef 元组):

 Card = (Suit(), Rank());

元组的其他作用

Records 解锁的另一项功能是返回多个值

函数现在可以返回一条元组,可以访问 2 个或更多值

在 Dart 3 之前,这需要创建另一个新类,并返回类来实现。

解构元组

Dart 3 还允许解构值,在可以将调用的结果直接分配给变量pickCard(也称为绑定 ):

final (card, deck) = pickCard;

上面的代码定义了 2 个新变量:card 和 deck全部写在一行中! 在 Dart 3 之前,这个过程要复杂得多

Switch 表达式和解构

可以将 switch 用作表达式(而不仅仅是一条语句)。

意味着可以将调用switch的结果直接分配给变量

此外,在switch情况下也可以进行解构,因此您可以在一行中进行模式匹配并提取值.

比如我们现在可以使用模式匹配从 Hand 中提取卡片,我们使用新的switch表达式语法以及解构元组

String howManyCards = switch (hand) { 
 /// Match `ThreeCards` and extract each card (`card1`, `card2`, `card3`)    ThreeCards(cards: (final card1, final card2, final card3)) => "3 cards: $card1, $card2, $card3",   
  /// Match `TwoCards` and extract each card (`card1`, `card2`) 
 TwoCards(cards: (final card1, final card2)) => "2 cards: $card1, $card2",   
 /// Match `OneCard` and extract it (`singleCard`) 
 OneCard(card: final singleCard) => "1 card: $singleCard",   
 /// Match `NoCard` (all cases are matched ✅)  
 NoCard() => "Ain't no card 🙌",
};
  • howManyCards 是一个新变量,包含switch 表达式的结果(不再是简单的语句☝️)
  • switch使用模式匹配来检查是否涵盖了所有可能的情况(否则会出现编译时错误)并从每个子类型中提取值
  • 在每个模式中,我们提取cards/card值并对其进行解构(将这些值分配给新变量card1card2card3singleCard)

增强的 if 语句

新语法还允许if语句中使用模式匹配

/// Pattern match and destructure directly inside `if` cases 
if (hand case OneCard(card: final singleCard)) {  
   print("1 card: $singleCard");
}

元组的使用规范

元组表达式

元组是使用元组表达式创建的:

var record = (number: 123, name: "Main", type: "Street");

这与函数调用参数列表的语法相同(带有 可选的 const 开头)。有一些语法限制 没有被语法捕获。如果元组具有以下任何一项,则这是一个编译时错误:

  • 同一字段名称多次。
  • 只有一个位置字段并且没有尾随逗号。
  • 无字段且尾随逗号。 不允许使用表达式(,)
  • 名为 hashCoderuntimeTypenoSuchMethod 或 toString 的字段。
  • 以下划线开头的字段名称。
  • 与位置的合成 getter 名称冲突的字段名称 场地。 例如:('pos', $1: 'named')因为命名字段“$1”是 与第一个位置字段的 getter 发生碰撞。

为了避免括号表达式产生歧义,元组带有只有一个位置字段必须有尾随逗号:

var number = (123);  // The number 123.
var record = (123,); // A record containing the number 123.

表达式()指的是没有字段的常量空元组。

元组类型注释

在类型系统中,每条元组都有对应的元组类型。元组类型 看起来类似于函数类型的参数列表。该类型被包围 括号并且可能包含逗号分隔的位置字段:

(int, String name, bool) triple;

每个字段都是一个类型注释和一个可选名称,该名称没有意义,但 对于文档目的很有用。

命名字段位于类型和名称对的大括号分隔部分内:

({int n, String s}) pair;

元组类型可以同时具有位置字段和命名字段:

(bool, num, {int n, String s}) quad;

如果元组类型具有以下任何一种,则这是一个编译时错误:

  • 同一字段名称多次。 即使其中一个或两个都成立,也是如此 碰撞场是位置性的。我们可以允许与位置发生碰撞 字段名称,因为它们仅用于文档,但我们不允许这样做 因为它令人困惑并且没有用。
  • 只有一个位置字段并且没有尾随逗号。 这并不含糊, 因为 Dart 中没有带括号的类型表达式。但禁止 这与元组表达式是对称的,并且有可能 稍后支持括号以在类型表达式中进行分组。
  • 名为 hashCoderuntimeTypenoSuchMethod 或 toString 的字段。
  • 以下划线开头的字段名称。
  • 与位置的合成 getter 名称冲突的字段名称 场地。 例如:(int, $1: int)因为命名字段“$1”是碰撞 与第一个位置字段的吸气剂。

有关更多详细元组的规范信息,您可以阅读元组的规范信息一文