记录类型 (元组)是 Dart 3 中引入的新功能。
您还需要在
pubspec.yaml
文件中将 Dart 3 设置为sdk
:sdk: ">=3.0.0 <4.0.0"
前言
在 dart 2 中,当您想要将多个对象捆绑到一个值中多个对象时,您有 2 个选择。
1:创建一个包含对象作为属性的类:
arduino
class DataClass {
final int number;
final String string;
final double float;
const DataClass(this.number, this.string, this.float);
}
如果对象过于太多的话,这个策略就会显得很冗长。
2:使用集合 ,例如List
、Map
,或Set
:
ini
List<Object> collection = [0, 'a', 0.0];
问题是该选项不是类型安全的:
List
、Map
和Set
可以存储单一类型(List<int>
、List<String>
或List<double>
)。 如果您需要多种类型,您通常会回退到List<Object>
记录类型 (元组) 在dart3的使用
csharp
(int, String, double) record = (0, 'a', 0.0);
// Or without type annotationfinal
record = (0, 'a', 0.0);
上面的代码创建了一条包含 int
、String
和 double
的元组,无需创建类或使用集合。
定义一个元组看起来与定义一个
List
完全相同,但用()
而不是[]
然后,您可以通过索引访问每个值(从 1 开始 ),前缀为 $
:
ini
(int, String, double) record = (0, 'a', 0.0);
int number = record.$1;
String string = record.$2;
double float = record.$3;
您还可以为元组中的每个值指定一个名称:
csharp
({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);
通过这样做,元组就像没有名称的类一样:您可以从其名称访问每个字段。
csharp
{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;
还可以使用命名字段和未命名字段的组合:
csharp
(int, double, {String string}) record = (1, string: 'a', 2.0);
此外,您还可以在一行中解构所有元组:
csharp
final (number, string, float) = record;
使用元组
我们使用新的元组类型将 Card
构造为 Suit
和 Rank
的组合:
ini
typedef Card = (Suit, Rank);
这个新的 ()
语法定义了一个 Card(在本例中为 Tuple
,因为它由 2 种类型组成)。 在 dart 3.0 之前,这是不可能的
注意 :typedef
允许为类型指定另一个名称,以后每次使用Card
而无需继续写(Suit, Rank)
,使得代码更加清晰。
这样可以避免为简单类型创建类 。在 dart 3.0 之前,相同的结果需要 class
:
kotlin
/// Before dart 3.0
class Card {
final Suit suit;
final Rank rank;
const Card(this.suit, this.rank);
}
使用这个新模型(sealed
和 typedef
元组):
ini
Card = (Suit(), Rank());
元组的其他作用
Records 解锁的另一项功能是返回多个值。
函数现在可以返回一条元组,可以访问 2 个或更多值。
在 Dart 3 之前,这需要创建另一个新类,并返回类来实现。
解构元组
Dart 3 还允许解构值,在可以将调用的结果直接分配给变量pickCard
(也称为绑定 ):
ini
final (card, deck) = pickCard;
上面的代码定义了 2 个新变量:card
和 deck
, 全部写在一行中! 在 Dart 3 之前,这个过程要复杂得多
Switch 表达式和解构
可以将 switch
用作表达式(而不仅仅是一条语句)。
意味着可以将调用
switch
的结果直接分配给变量。
此外,在switch
情况下也可以进行解构,因此您可以在一行中进行模式匹配并提取值.
比如我们现在可以使用模式匹配从 Hand
中提取卡片,我们使用新的switch
表达式语法以及解构元组
php
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
值并对其进行解构(将这些值分配给新变量card1
,card2
,card3
,singleCard
)
增强的 if 语句
新语法还允许在if
语句中使用模式匹配:
php
/// Pattern match and destructure directly inside `if` cases
if (hand case OneCard(card: final singleCard)) {
print("1 card: $singleCard");
}
元组的使用规范
元组表达式
元组是使用元组表达式创建的:
less
var record = (number: 123, name: "Main", type: "Street");
这与函数调用参数列表的语法相同(带有 可选的 const
开头)。有一些语法限制 没有被语法捕获。如果元组具有以下任何一项,则这是一个编译时错误:
- 同一字段名称多次。
- 只有一个位置字段并且没有尾随逗号。
- 无字段且尾随逗号。 不允许使用表达式
(,)
。 - 名为
hashCode
、runtimeType
、noSuchMethod
或toString
的字段。 - 以下划线开头的字段名称。
- 与位置的合成 getter 名称冲突的字段名称 场地。 例如:
('pos', $1: 'named')
因为命名字段"$1"是 与第一个位置字段的 getter 发生碰撞。
为了避免括号表达式产生歧义,元组带有只有一个位置字段必须有尾随逗号:
ini
var number = (123); // The number 123.
var record = (123,); // A record containing the number 123.
表达式()
指的是没有字段的常量空元组。
元组类型注释
在类型系统中,每条元组都有对应的元组类型。元组类型 看起来类似于函数类型的参数列表。该类型被包围 括号并且可能包含逗号分隔的位置字段:
arduino
(int, String name, bool) triple;
每个字段都是一个类型注释和一个可选名称,该名称没有意义,但 对于文档目的很有用。
命名字段位于类型和名称对的大括号分隔部分内:
arduino
({int n, String s}) pair;
元组类型可以同时具有位置字段和命名字段:
dart
(bool, num, {int n, String s}) quad;
如果元组类型具有以下任何一种,则这是一个编译时错误:
- 同一字段名称多次。 即使其中一个或两个都成立,也是如此 碰撞场是位置性的。我们可以允许与位置发生碰撞 字段名称,因为它们仅用于文档,但我们不允许这样做 因为它令人困惑并且没有用。
- 只有一个位置字段并且没有尾随逗号。 这并不含糊, 因为 Dart 中没有带括号的类型表达式。但禁止 这与元组表达式是对称的,并且有可能 稍后支持括号以在类型表达式中进行分组。
- 名为
hashCode
、runtimeType
、noSuchMethod
或toString
的字段。 - 以下划线开头的字段名称。
- 与位置的合成 getter 名称冲突的字段名称 场地。 例如:
(int, $1: int)
因为命名字段"$1"是碰撞 与第一个位置字段的吸气剂。
有关更多详细元组的规范信息,您可以阅读元组的规范信息一文