Dart 中冻结、重载构造函数或相关联合值 union 的区分类型联合

问题描述 投票:0回答:1

我想知道在Flutter / Dart中是否有任何好的方法来解决以下问题。

我想使用冻结的包将 Result 类型定义为可区分联合。

我将介绍一下我目前拥有的内容,但首先是所需的功能:

  • 必须是可区分的联合,以便在这两个“主要”选项之间完全区分SuccessError
  • 拥有更具体或受限制的成功类型“重载”类型,以便可以进一步缩小范围并以不同的方式处理。 (有些方法只需要继续流程,而其他方法可能需要显示“成功”对话框)
  • 至少一种成功类型应该支持泛型构造函数,这样如果之前某个操作成功,则可以处理任何类型的返回值。

我会告诉你我的意思。 我有以下代码。

@freezed
sealed class Result<T> with _$Result<T> {
  const Result._();

  // default success constructor with no value and no message, and is used only 
  // to indicate a successful operation
  const factory Result.success() = Success;

  // success constructor with a generic value resulting from the operation
  const factory Result.successValue(T value) = SuccessValue<T>;

  // success constructor with an explicit message and an optional generic value
  // can be used in UI elements like Dialogs, Snackbars, etc.
  const factory Result.successMessage(String message, [T? value]) =
      SuccessMessage<T>;

  // error constructor with an optional message
  const factory Result.error([String? message]) = Error;

  bool get isSuccess =>
      this is Success || this is SuccessMessage || this is SuccessValue;
}

我觉得在成功比较方面我缺乏一种“类型安全”。 我真正想要的是所有构造函数类型“Success |”成功价值 | SuccessMessage ' 都是相关的,并被认为是通用的 'Success' 类型。 它们要么应该是相同类型的重载,要么以某种方式在父“Success”类型之间继承,并且应该定义更受限制的子类型“SuccessValueSuccessMessage”。

这就是我公开 isSuccess getter 的原因,但开发人员仍然可能会犯这样的错误:可能会做出错误的比较“结果是 Success”。

flutter dart generics discriminated-union freezed
1个回答
0
投票

感觉有点像黑客,但您可以使用

@Implements
注释将所有成功类型连接在一起:

import 'package:freezed_annotation/freezed_annotation.dart';

part 'freezed_result_other.freezed.dart';

sealed class Success<T> {}

@freezed
sealed class Result<T> with _$Result<T> {
  @Implements<Success<T>>()
  factory Result.successEmpty() = SuccessEmpty<T>;

  @Implements<Success<T>>()
  factory Result.successVal(T value) = SuccesVal<T>;

  @Implements<Success<T>>()
  factory Result.sucessMessage(String message, [T? value]) = SuccessMessage<T>;

  factory Result.error([String? message]) = OError;
}

void example() {
  final Result<int> result = bar();
  switch (result) {
    case Success<int> s:
      switch (s) {
        case SuccessEmpty<int>():
        // TODO: Handle this case.
        case SuccesVal<int>(:final int value):
        // TODO: Handle this case.
        case SuccessMessage<int>(:final String message, :final int? value):
        // TODO: Handle this case.
      }
    case OError<int>():
    // TODO: Handle this case.
  }
}

也就是说,拥有三种不同的结果类型让我觉得很奇怪。对于没有值的成功、有值的成功以及有消息和可选值的成功,是否有特定的原因需要单独的变体?如果调用可以成功返回而没有值,则泛型类型可能只是可为空或具有自己的

None
变体的另一个可区分联合。如果您有一个返回成功且从不包含值的函数,那么只需让它返回类型
Result<void>
即可。我还会删除携带消息的变体,并将
message
设为
Success
上的可为空字段。这给我们留下了:

import 'package:freezed_annotation/freezed_annotation.dart';

part 'freezed_result.freezed.dart';

@freezed
sealed class Result<T> with _$Result<T> {
  factory Result.success(T result, [String? message]) = Success<T>;
  factory Result.error([String? message]) = Error;
}

void example() {
  final Result<int?> result = foo();
  switch (result) {
    case Success<int?>(:final result?, :final message?):
      print('Non-null result with message: $result ($message)');
    case Success<int?>(:final result?):
      print('Non-null result: $result');
    case Success<int?>(:final message?):
      print('Null result with message: $message');
    case Success<int?>():
      print('Null result with no message');
    case Error<int?>(:final message?):
      print('Error with message: $message');
    case Error<int?>():
      print('Error with no message');
  }
}

Result<int?> foo() {
  throw UnimplementedError();
}

也可以仅使用密封类编写任意一种不带 Freezed 的变体。这是更简单的:

sealed class Result<T> {
  const Result();
}

class Success<T> extends Result<T> {
  const Success(this.value, [this.message]);

  final T value;
  final String? message;
}

class Error<T> extends Result<T> {
  const Error([this.message]);

  final String? message;
}

© www.soinside.com 2019 - 2024. All rights reserved.