如何使dart函数通用?

5ktev3wc  于 2023-09-28  发布在  其他
关注(0)|答案(2)|浏览(90)

我有一个基本的FirebaseManager,目前它只解析并返回一个User()列表。我如何让它解析并返回任何类?
应该是这样的:

final result = await manager.read<User>();
  final result = await manager.read<Address>();
  final result = await manager.read<Contacts>();

当前管理器的用法。

Future _firebaseFirestoreTest() async {
  final manager = FirebaseManager(CollectionName.newAction);
  final result = await manager.read();
  print(result);
}
class FirebaseManager implements DataSourceRepository {
  final CollectionReference _collection;

  FirebaseManager(String collectionName)
      : _collection = FirebaseFirestore.instance.collection(collectionName);

  @override
  Future<List<User>> read() async {
    final snapshot = await _collection.get();
    final data = <User>[];

    for (final document in snapshot.docs) {
      final json = document.data() as Map<String, dynamic>;
      final object = User.fromJson(json);
      data.add(object);
    }

    return data;
  }
}
bihw5rsg

bihw5rsg1#

遗憾的是,在Dart中(或者至少在Flutter中)不可能以通用的方式做到这一点。类的静态成员不是泛型函数可用的类型参数的一部分,因此无法在泛型类型上调用工厂构造函数或静态方法。实现此功能最直接的方法是向函数传递一个解析回调:

Future<List<T>> read<T>(T Function(Map<String, dynamic>) parser) async {
  final snapshot = await _collection.get();
  final data = <User>[];

  for (final document in snapshot.docs) {
    final json = document.data() as Map<String, dynamic>;
    final object = parser(json);
    data.add(object);
  }

  return data;
}
final result = await manager.read<User>(User.fromJson);
final result = await manager.read<Address>(Address.fromJson);
final result = await manager.read<Contacts>(Contacts.fromJson);

有一种稍微方便一点的方法,尽管它需要一些设置,并且不能动态使用。不要将解析方法传递给read函数,而是保留一个已知解析函数的类型查找表。然后,您可以直接在read函数中使用泛型类型作为键:

typedef T _Parser<T>(Map<String, dynamic> map);
final Map<Type, _Parser> _parsers = {
  User: User.fromJson,
  Address: Address.fromJson,
  Contacts: Contacts.fromJson,
};

class FirebaseManager implements DataSourceRepository {
  // ...
  
  @override
  Future<List<T>> read<T>() async {
    final parser = _parsers[T];
    if (parser == null) {
      throw ArgumentError('Unsupported parsable type $T');
    }
    
    final snapshot = await _collection.get();
    final data = <T>[];

    for (final document in snapshot.docs) {
      final json = document.data() as Map<String, dynamic>;
      final object = parser(json) as T;
      data.add(object);
    }

    return data;
  }
}
final result = await manager.read<User>();
final result = await manager.read<Address>();
final result = await manager.read<Contacts>();
piok6c0g

piok6c0g2#

此解决方案不完全有效。看看上面@Abion47的另一个答案^

您可以使用泛型部分地实现这一点。请在此处查看文档:https://dart.dev/language/generics
对于你的例子,你可以在调用read函数时包含一个类型T,这样它就知道返回这个类型(如果没有传递类型,它将只使用并返回dynamic,我猜这在fromJson中会失败)。

Future<List<T>> read<T>() async {
  ...
  final data = <T>[];
  ...
    final object = T.fromJson(json);
  ...
  return data;
}

你会这样称呼它:

final result = await manager.read<User>(); // type of result becomes List<User>

更新

我的第一个想法是考虑到你的更新:因为isJson没有为泛型类型T定义,也许你可以为你所有的可序列化类定义一个共享的超类型?问题是fromJson是一个静态方法,这些方法不能被子类继承。
所以我不知道该怎么做。让我们希望有人更了解这个主题,可以帮助我们弄清楚这一点。

更新#2

看看这篇SO文章,在Dart中不可能在泛型类型上调用静态方法:Calling a static method from a generic constraint Dart

相关问题