Contents

Extension methods

Dart 2.7中引入的扩展方法是一种向现有库添加功能的方法. 您可能甚至不知道使用扩展方法. 例如,当您在IDE中使用代码完成功能时,它建议将扩展方法与常规方法一起使用.

如果您想通过观看视频来学习,这里是扩展方法的很好概述.

Overview

当您使用其他人的API或实现广泛使用的库时,更改API通常是不切实际或不可能的. 但是您可能仍想添加一些功能.

例如,考虑以下将字符串解析为整数的代码:

int.parse('42')

将功能设置在String上可能会更好(更短,更易于使用工具):

'42'.parseInt()

要启用该代码,可以导入一个包含String类扩展的库:

import 'string_apis.dart';
// ···
print('42'.parseInt()); // Use an extension method.

扩展不仅可以定义方法,还可以定义其他成员,例如getter,setter和operator. 另外,扩展名具有名称,如果出现API冲突,这将很有帮助. 这是您如何使用对字符串进行操作的扩展(名为NumberParsing )来实现扩展方法parseInt()方法:

extension NumberParsing on String {
  int parseInt() {
    return int.parse(this);
  }
  // ···
}
lib/string_apis.dart

下一节将介绍如何使用扩展方法. 之后是有关实现扩展方法的部分.

Using extension methods

像所有Dart代码一样,扩展方法在库中. 您已经了解了如何使用扩展方法-只需导入其中的库,然后像普通方法一样使用它:

// Import a library that contains an extension on String.
import 'string_apis.dart';
// ···
print('42'.padLeft(5)); // Use a String method.
print('42'.parseInt()); // Use an extension method.

这就是使用扩展方法通常需要知道的. 在编写代码时,您可能还需要知道扩展方法如何依赖静态类型(而不是dynamic类型)以及如何解决API冲突 .

Static types and dynamic

您不能对dynamic类型的变量调用扩展方法. 例如,以下代码导致运行时异常:

dynamic d = '2';
print(d.parseInt()); // Runtime exception: NoSuchMethodError

扩展方法可以与Dart的类型推断一起使用. 以下代码很好,因为推断变量v具有String类型:

var v = '2';
print(v.parseInt()); // Output: 2

dynamic不起作用的原因是扩展方法针对接收方的静态类型进行了解析. 由于扩展方法是静态解析的,因此它们与调用静态函数一样快.

有关静态类型和dynamic类型的更多信息,请参见Dart类型系统 .

API conflicts

如果扩展成员与接口或另一个扩展成员冲突,那么您可以有一些选择.

一种选择是使用showhide来限制公开的API,从而更改导入冲突扩展名的方式:

// Defines the String extension method parseInt().
import 'string_apis.dart';

// Also defines parseInt(), but hiding NumberParsing2
// hides that extension method.
import 'string_apis_2.dart' hide NumberParsing2;

// ···
// Uses the parseInt() defined in 'string_apis.dart'.
print('42'.parseInt());

另一种选择是显式应用扩展,这将导致代码看起来像扩展是包装类:

// Both libraries define extensions on String that contain parseInt(),
// and the extensions have different names.
import 'string_apis.dart'; // Contains NumberParsing extension.
import 'string_apis_2.dart'; // Contains NumberParsing2 extension.

// ···
// print('42'.parseInt()); // Doesn't work.
print(NumberParsing('42').parseInt());
print(NumberParsing2('42').parseInt());

如果两个扩展名都相同,则可能需要使用前缀导入:

// Both libraries define extensions named NumberParsing
// that contain the extension method parseInt(). One NumberParsing
// extension (in 'string_apis_3.dart') also defines parseNum().
import 'string_apis.dart';
import 'string_apis_3.dart' as rad;

// ···
// print('42'.parseInt()); // Doesn't work.

// Use the ParseNumbers extension from string_apis.dart.
print(NumberParsing('42').parseInt());

// Use the ParseNumbers extension from string_apis_3.dart.
print(rad.NumberParsing('42').parseInt());

// Only string_apis_3.dart has parseNum().
print('42'.parseNum());

如示例所示,即使使用前缀导入,也可以隐式调用扩展方法. 唯一需要使用前缀的方法是在显式调用扩展名时避免名称冲突.

Implementing extension methods

使用以下语法创建扩展:

extension <extension name> on <type> {
  (<member definition>)*
}

例如,以下是在String类上实现扩展的方法:

extension NumberParsing on String {
  int parseInt() {
    return int.parse(this);
  }

  double parseDouble() {
    return double.parse(this);
  }
}
lib/string_apis.dart

要创建仅在声明其的库中可见的本地扩展名,请省略扩展名或为其指定一个以下划线( _ )开头的名称.

扩展的成员可以是方法,获取器,设置器,运算符. 扩展也可以具有静态字段和静态帮助器方法.

Implementing generic extensions

扩展可以具有通用类型参数. 例如,下面的代码使用吸气剂,运算符和方法扩展了内置List<T>类型:

extension MyFancyList<T> on List<T> {
  int get doubleLength => length * 2;
  List<T> operator -() => reversed.toList();
  List<List<T>> split(int at) => <List<T>>[sublist(0, at), sublist(at)];
}

类型T是基于调用方法的列表的静态类型进行绑定的.

Resources

有关扩展方法的更多信息,请参见以下内容:

by  ICOPY.SITE