在Dart 2.6的最新版本中,该语言引入了新功能,即静态扩展或静态扩展方法,该功能使您可以向现有类型添加新方法。 为什么需要扩展? 如何使用它们以及它们有什么用?

引言
首先,扩展一般是什么?
扩展是语法糖,用于在与类声明模块不同的位置扩展现有的类。
在编程中,扩展方法已经存在很长时间了,所以他们开始大胆尝试。 扩展在诸如C#,通过Manifold,Swift,Kotlin和许多其他语言的Java之类的语言中得到积极使用。
问题
假设我们有一个catchError方法,该方法很糟糕,需要将其重写为新的出色函数。 假设他使用任何类型的函数作为参数,而不是严格类型化的函数或函数类型检查,这是因为8个月前开发此功能时,这是合乎逻辑的。
首先想到的是重写此功能,但是这里我们面临的问题是,它经常在项目中发生,更改功能将导致整个项目无法运行。
好吧,如果第一种选择不适合我们。 出于逻辑原因,我可以实施一个新的Future功能,以满足我的所有要求。
abstract class Future<T> { ... /// Catches any [error] of type [E]. Future<T> onError<E>(FutureOr<T> handleError(E error, StackTrace stack)) => this.catchError(... - ...); } ... }
我会这样称呼她:
Future<String> someString = ...; someString.onError((FormatException e, s) => ...).then(...);
不幸的是,我无法将此函数添加到Future类中。 如果这样做,我也将其添加到Future接口中,并且实现此接口的任何其他类将是不完整的,并且将不再编译。
好吧,另一种选择是实现第三方功能,如下所示:
Future<T> onFutureError<T, E>(Future<T> source, FutureOr<T> handleError(E error, StackTrace stack)) => source.catchError(... - ...);
她的电话看起来像这样:
Future<String> someString = ...; onFutureError(someString, (FormatException e, s) => ...).then(...);
太好了,一切正常! 但可悲的是,它开始被人们广泛阅读。 我们使用方法。 它们在类内部实现,因此称为-.doingSomething(); 这段代码是可以理解的,我只是从左到右阅读它,并在脑海中站着一系列事件。 使用辅助函数会使代码繁琐且可读性较差。
那么,我可以实现一个新类,并让用户使用改进的功能包装其旧界面。
class CustomFuture<T> { CustomFuture(Future<T> future) : _wrapper = future; Future<T> _wrapper; Future<T> onError<E>(FutureOr<T> handleError(E error, StackTrace stack)) => _wrapper.catchError(...- ...); }
呼叫将如下所示:
Future<String> someString = ...; CustomFuture(someString).onError((FormatException e, s) => ...).then(...);
看起来很棒!
解决扩展问题
一旦我们停止以Pascal进行编程并返回2019年,此功能的实现将缩减为以下大小:
extension CustomFuture <T> on Future<T> { Future<T> onError<E>( FutureOr<T> handleError(E error, StackTrace stack)) => this.catchError(...something clever...); }
通话如下所示:
Future<String> someString = ...; someString.onError((FormatException e, s) => ...).then(...);
仅此而已! 该问题的解决方案仅用了5行代码。 你呢 您可能想知道什么是魔术以及它是如何工作的。
实际上,它的行为与包装器类相同,尽管实际上它只是一个辅助
静态函数。 扩展允许您放弃显式的包装编写。
这不是包装器
扩展设计的工作方式类似于现有类的声明,但其作用就像是使用私有_wrapper的包装器一样。 但是,与包装器类相比,有一个优势,那就是直接访问类本身,而不是访问_wrapper包装器类。
此功能并不是为了实现功能而创建的,但是正如我之前所说的,扩展确实是调用静态函数的一种更方便的方法。 这意味着没有包装对象。
都是静态的
我在上面说了“静态扩展方法”,我这样做是有原因的!
Dart是静态类型的。 编译器在编译时就知道每个表达式的类型,因此,如果您编写user.age(19)并且age是扩展名,那么编译器必须找出给定对象中包装了哪种类型,才能找到整个调用的类型。
会出现什么问题?
有关扩展问题的最简单示例是,您的扩展范围中有多个扩展。 基本上,获胜者是最接近您正在呼叫成员的实际表达类型的扩展名,但有保留。
解决问题的最简单方法是严格连接所需的扩展,也可以显式使用该扩展:
... List list = ...; MyList(list).printlist(); SomeList(list).printlist(); ... extension MyList on List { void printlist() { print(...- ...); } } extension SomeList on List { void printlist() { print(...- ...); } }
总结
- dart语言具有用于扩展现有功能的便捷工具。
- 您可以扩展方法,运算符,setter和getter,但不能扩展字段。
- 您可以显式调用扩展方法,或者-与接口成员或其他扩展不存在隐式调用时。
- 隐式调用就像显式调用一样工作。
- 扩展名是静态的。 关于它们的所有内容都基于静态类型解决。
如果扩展名的输出由于扩展名冲突而失败,则可以执行以下操作之一:
- 显式应用扩展名。
- 导入有前缀的冲突扩展名,因为这样扩展名不可用于隐式调用。
- 根本不要导入有冲突的扩展名。
仅此而已! 您可以充分利用扩展功能。
当然,还有有用的链接:
网站混乱Dart网站在哪里可以阅读有关扩展的更多信息电报频道,我不仅谈论Flutter世界中的所有最新消息