Dart 中 Future 的向下泛型

发布于 2025-01-15 00:07:03 字数 2065 浏览 0 评论 0原文

我有一个具有通用参数的 future,它是另一个类 (B extends A) 的超类 (A)。我知道 Future 的值的实例是子类型。为什么我不能在 dart 中将 Future 向下转换为 Future ?如果我打开 Future 一次,然后使用 async/await 再次包装它,它就可以工作。

这是一个例子:

class A {}
class B extends A{}

void main() {
  Future<A> getFuture() async { return B();}
  Future<B> getBasA() { return getFuture() as Future<B>;}
  Future<B> getBasAasync() async { return (await getFuture()) as B;}

  print(getBasAasync()); // Works
  print(getBasA()); // Throws at runtime
}

为了好奇并作为问题的动机,这里有一个更贴近现实的例子。我有一个发出数据包的流,我对其进行过滤,然后得到第一个数据包,如下所示:

Future<T> getResponse<T extends ReceivedPacket>() =>
  getStream<ReceivedPacket>().firstWhere((packet) => packet is T) as Future<T>; //throws

Future<T> getResponse<T extends ReceivedPacket>() async { //works
  return (await  getStream<ReceivedPacket>().firstWhere((packet) => packet is T)) as T;
} 

PS:我已经在 Typescript (将愉快地编译和运行)和 C# (无法编译,但我的 C# 知识非常有限)。我知道这个问题的答案可能是“因为这就是 dart 类型系统的工作原理”。我只是很困惑,因为我预计它要么像 C# 一样在编译时失败,要么像打字稿一样在运行时工作。

I have a future that has a generic parameter, which is a superclass (A) of another class (B extends A). I know for a fact that the instance of the value of the Future is of the subtype. Why can't I downcast the Future<A> to Future<B> in dart? If I unwrap the Future once and then wrap it again using async/await, it works.

Here's an example:

class A {}
class B extends A{}

void main() {
  Future<A> getFuture() async { return B();}
  Future<B> getBasA() { return getFuture() as Future<B>;}
  Future<B> getBasAasync() async { return (await getFuture()) as B;}

  print(getBasAasync()); // Works
  print(getBasA()); // Throws at runtime
}

For the curious and as a motivation for the question, here's a closer-to-world example. I have a stream that emits data packets, which I filter and then get the first like this:

Future<T> getResponse<T extends ReceivedPacket>() =>
  getStream<ReceivedPacket>().firstWhere((packet) => packet is T) as Future<T>; //throws

Future<T> getResponse<T extends ReceivedPacket>() async { //works
  return (await  getStream<ReceivedPacket>().firstWhere((packet) => packet is T)) as T;
} 

PS: I've tried it out in Typescript (will happily compile and run) and C# (won't compile, but I have very limited C# knowledge). I understand that the answer to this question might be "because this is how the dart type system works". I'm just confused, because I'd have expected it either to fail at compile time like C# or work at runtime, too, like typescript.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

唯憾梦倾城 2025-01-22 00:07:03

您将 getFuture() 声明为返回 Future 但使用了 async 关键字,因此 Dart 会自动转换 return B();(本质上)返回 Future.value(B());。返回的 Future 从来就不是 Future,因此不能向下转型为它。

显式创建 Future 将会达到您的预期:

Future<A> getFuture() { return Future<B>.value(B()); }

您可能会认为 Dart 在 async 函数中转换 return x; 时,它应该创建一个 < code>Future其中 Tx 的静态类型,而不是从函数的返回类型推断它。正如lrn在评论中解释的那样,这是不可能的,因为你可能有:

class C extends A {}

Future<A> getFuture() async {
  await Future.delayed(const Duration(seconds: 1));

  if (Random().nextBool()) {
    return B();
  } else {
    return C();
  }
}

调用者必须立即返回一个Future,但不知道它的值是否是一个BC 直到 Future 最终完成。

我原以为它会像 C# 一样在编译时失败

我对 C# 的经验也非常有限,但我认为 C# 会给你一个编译错误,因为 C# 不考虑 GenericGeneric 的子类型,而 Dart 则是。

You declared getFuture() as returning Future<A> but with the async keyword, so Dart automatically transforms return B(); to (essentially) return Future<A>.value(B());. The returned Future was never a Future<B> and therefore cannot be downcast to it.

Creating the Future explicitly would do what you expect:

Future<A> getFuture() { return Future<B>.value(B()); }

You could argue that Dart when transforms return x; in async functions, it should create a Future<T> where T is the static type of x instead of inferring it from the function's return type. As lrn explained in comments, that can't be done because you might have:

class C extends A {}

Future<A> getFuture() async {
  await Future.delayed(const Duration(seconds: 1));

  if (Random().nextBool()) {
    return B();
  } else {
    return C();
  }
}

The caller must get a Future back immediately, but it won't be known whether its value will be a B or C until the Future eventually completes.

I'd have expected it either to fail at compile time like C#

I too have very limited experience with C#, but I think that C# gives you a compilation error because C# does not consider Generic<SubType> to be a subtype of Generic<SuperType> whereas Dart does.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文