我想知道在Flutter / Dart中是否有任何好的方法来解决以下问题。
我想使用冻结的包将 Result 类型定义为可区分联合。
我将介绍一下我目前拥有的内容,但首先是所需的功能:
我会告诉你我的意思。 我有以下代码。
@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”类型之间继承,并且应该定义更受限制的子类型“SuccessValue和SuccessMessage”。
这就是我公开 isSuccess getter 的原因,但开发人员仍然可能会犯这样的错误:可能会做出错误的比较“结果是 Success”。
感觉有点像黑客,但您可以使用
@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;
}