飞镖扩展(颤振)

在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,但不能扩展字段。
  • 您可以显式调用扩展方法,或者-与接口成员或其他扩展不存在隐式调用时。
  • 隐式调用就像显式调用一样工作。
  • 扩展名是静态的。 关于它们的所有内容都基于静态类型解决。

如果扩展名的输出由于扩展名冲突而失败,则可以执行以下操作之一:

  1. 显式应用扩展名。
  2. 导入有前缀的冲突扩展名,因为这样扩展名不可用于隐式调用。
  3. 根本不要导入有冲突的扩展名。

仅此而已! 您可以充分利用扩展功能。

当然,还有有用的链接:

网站混乱
Dart网站
在哪里可以阅读有关扩展的更多信息
电报频道,我不仅谈论Flutter世界中的所有最新消息

Source: https://habr.com/ru/post/zh-CN476930/


All Articles