【问题标题】:Why an explicit ".cast<>()" function in Dart instead of "as <>"为什么在 Dart 中使用显式的“.cast<>()”函数而不是“as <>”
【发布时间】:2018-09-07 13:49:29
【问题描述】:

在我的问题Dart 2.X List.cast() does not compose 中,答案需要将List&lt;dynamic&gt; 转换为List&lt;String&gt;,如下所示:

List<String> ls = (json['data'] as List).cast<String>().map((s) => s.toUpperCase()).toList();

我的其他语言经验让我先写了这个:

List<String> ls = (json['data'] as List<String>).map((s) => s.toUpperCase()).toList();

请注意,这会编译,但在 Dart 2 中运行时会失败。

为什么 List 的 Dart 类型转换需要函数 as List).cast&lt;String&gt;() 而不是简单地使用 Dart as “类型转换运算符”,例如 as List&lt;String&gt;

---- 编辑----

我正在使用最新的 Dart 2.0.0-dev.43.0 并使用 as 类型转换/断言获得不一致的运行时行为。 .cast&lt;&gt;() 函数创建新的可迭代对象与.map() 不一样吗?将我的代码更改为此有效:

List<String> ls = (json['data'] as List).map((s) => (s as String).toUpperCase()).toList();

这似乎利用了List 的第一个演员是List&lt;dynamic&gt;。因此.map 函数参数也是dynamic

我上面的第二个例子 as List&lt;String&gt; 在我们代码的某些地方工作,但在其他地方却不行。请注意,IntelliJ 正确推断了上述所有示例中的类型 - 它是发生故障的运行时。我猜测不一致的行为是由于 Dart 2.x 仍在开发中。

----第二次编辑----

这是我在一个类构造函数中的测试用例:

Map<String, dynamic> json = { "data": ["a", "b", "c"] };

//List<String> origBroken = json["data"].map( (s) => s.toUpperCase() ).toList();

// Sometimes works - sometimes gives "Ignoring cast fail from JSArray to List<String>" at runtime!!!
List<String> wonky = (json["data"] as List<String>).map( (s) => s.toUpperCase() ).toList();
print("Wonky $wonky");

List<String> fix1 = (json["data"] as List).cast<String>().map( (s) => s.toUpperCase() ).toList();
List<String> fix2 = (json["data"] as List).map( (s) => (s as String).toUpperCase() ).toList();
List<String> explicit2 = (json["data"] as List<dynamic>).map( (dynamic s) => (s as String).toUpperCase() ).toList();

// From accepted answer of the linked question - compile error because .cast() doesn't take parameters
//   error: Too many positional arguments: 0 expected, but 1 found.
//List<String> notBroken = (json['data'] as List).cast<String>((s) => s.toUpperCase()).toList();
List<String> notBrokenFixed = (json['data'] as List<String>).cast<String>().map((String s) => s.toUpperCase()).toList();

问题是 Ignoring cast fail from JSArray to List&lt;String&gt; 有时由 wonky 赋值给出的警告。当我说有时是因为当我对使用包含此代码的库的主应用程序进行更改时,它会发生不可预测的变化 - 而不对此类甚至库进行更改。

在我编写上述第一个编辑时,wonky 无法正常工作。我现在又试了一次,它正在工作。我没有更改此库中的任何代码 - 我一直在依赖于此代码库的主应用程序中工作。

一些背景知识,这是一个从 Angular/Typescript 转换而来的多库项目。这些测试用例基于我们将 JSON 反序列化为 Dart 类的处理。我们使用类构造函数初始化器将 JSON(动态)字符串映射到各种数据结构,例如枚举、Option 和 Either(来自 dartz)。

几周前,运行时警告开始发生,我相信是因为Breaking Change: --preview-dart-2 turned on by default。 我知道这个警告很快就会出错。所以我将警告追溯到这些从 JSON 动态数据映射的转换(是的,动态数据是 Dart 中的一个边缘情况,但它是 dart:convert 提供的)。

我们正在使用 DDC 和最新的 Dart 2.0.0-dev.43.0、angular 5.0.0-alpha+8、build_runner 0.8.0、IntelliJ 2018.1 在 Mac 上进行开发,并在 Chrome 65.0.3325.181 上运行。

----最终编辑----

导致此问题的当前开发构建/运行时存在不稳定性。不,我没有可重现的例子。更改和重建我们的主应用程序会导致此代码在未修改的库依赖项中有时会发出运行时警告Ignoring cast fail from JSArray to List&lt;String&gt;

来自这个问题原始部分的可疑代码(也是上面的wonky

List<String> ls = (json['data'] as List<String>).map((s) => s.toUpperCase()).toList();

将动态 JSON 数据转换为 List&lt;String&gt;。类型受到完全限制,Dart 分析器/IntelliJ 将 s 推断为 Static type: String

有时出现的运行时警告和使用.cast() 的相关答案是导致这个问题的原因。这时候我会相信分析器并忽略运行时警告。

【问题讨论】:

  • “在我们代码的某些地方有效,但在其他地方无效。”您能否提供具体的例子,其中它不起作用以及具体错误是什么?

标签: dart


【解决方案1】:

在 Dart 2 中,泛型类型被具体化了。

as ... 更像是一个断言,如果值类型不匹配 as 会导致运行时异常。

cast&lt;T&gt;() 是 Dart 2 中在 Iterable 上引入的一种方法,它实际上创建了一个类型为 Iterable&lt;T&gt; 的新迭代(或在您的情况下为子类 List&lt;T&gt;),其中填充了原始 interable 的值。


更新

您可以使用print('wonky: ${wonky.runtimeType}'); 查看实际类型是什么。

如果类型符合您的要求,您可以使用as 将其传达给分析器,表明可以安全地采用这种类型。

如果类型不匹配,例如因为它是List 而不是List&lt;String&gt;,那么您可以使用.cast&lt;String&gt;() 将其实际设为List&lt;String&gt;

List<String> broken = (json['data'] as List)
  .cast<String>((s) => s.toUpperCase()).toList();

在这里,您似乎尝试使用cast 进行转换和映射,但这是无效的。 map() 两者都可以

List<String> notBroken = (json['data'] as List)
  .map<String>((s) => s.toUpperCase()).toList();

【讨论】:

  • 很抱歉在我的示例中包含notBroken 代码。 @matanlurey 在回答问题 stackoverflow.com/questions/49539879/… 时提供了使用 cast 之类的 map 的代码。我把它上面的编译错误注释掉了。
  • 我很难弄清楚到底什么是你做不到的。我的更新是否帮助您了解ascast&lt;T&gt;()map&lt;T&gt;(...) 之间的区别?
  • 感谢您的回答。请参阅上面的“最终编辑”。我对“之间的差异”(Scala 开发)没有问题。我一直遇到不一致的构建/运行时行为的问题。是的,我知道我处于最前沿。
  • 我想你现在明白了 你可以尝试analysis_options.yaml 中的implicit-cast: false 来限制分析器接受的更多内容。
  • 另外,请注意根据 Effective Dart 指南,“当附近的操作(如 List&lt;T&gt;.from())可以使用时,不要使用 cast()。”这是因为cast() 方法返回一个惰性集合,该集合会检查每个操作的元素类型。如果您只对少数元素执行少量操作,那么这种懒惰可能会很好。但在许多情况下,惰性验证和包装的开销超过了好处。 ~ 来源:dartlang.org/guides/language/effective-dart/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-21
  • 2023-01-30
  • 1970-01-01
  • 2016-10-06
  • 1970-01-01
  • 2011-04-18
相关资源
最近更新 更多