我们如何从泛型函数中获取基于对象 属性 的对象索引? |镖

How can we get object index based on object property from a generic function? | Dart

我已经为我的数据库 crud 操作设置了一组很好的通用函数。我需要对一些专门功能进行更细粒度的控制。我希望能够按 属性 搜索数据库对象列表。似乎不可能,有一个警告 - 所有对象都将具有 属性 的 uuid,这是我想要搜索的。 Sooo... SO 的一些天才头脑一定是可能的。

当然,我想做这样的事情:

Future<int> getExampleIndexByUUID({required String uuid}) async 
  => await Hive.openBox<Example>('Example_Box')
     .then((box) => box.values.toList().indexWhere(example)
       => example.uuid == uuid);

但是以上对于泛型类型是不可能的:

Future<T> getExampleIndexByUUID<T>({
  required T objectType,
  required String uuid,
  }) async => await Hive.openBox<T>(objectDatabaseNameGetter(objectType))
  .then((box) => box.values.toList().indexWhere(example)
    => example... );                    // Dead end- no property access here

PS 我知道我可以在通用函数之外创建方法来处理这个问题。我还可以创建另一个大型 switch case 来处理这个问题,但这是我想要避免的。我想学习在这种情况下更好地抽象我的代码。任何帮助或指点表示赞赏!如果我唯一的选择是有一个开关盒或在函数之外做工作,那就这样吧。

  • 定义一个公共接口。

    最好的方法是让所有具有 uuid 属性 的 classes 共享一个公共基础 class,然后您的通用函数可以将其类型参数限制为 class:

    的子类型
    abstract class HasUuid {
      String get uuid;
    }
    
    class Example implements HasUuid {
      @override
      String uuid;
    
      Example(this.uuid);
    }
    
    Future<int> getExampleIndexByUUID<T extends HasUuid>({
      required T objectType,
      required String uuid,
    }) async {
      var box = await Hive.openBox<T>(objectDatabaseNameGetter(objectType));
      return box.values.toList().indexWhere(
        (example) => example.uuid == uuid),
      );
    }
    
  • 使用回调。

    如果您不控制要使用的 classes,您可以让通用函数接受回调而不是检索所需的 属性。这对呼叫者来说会更麻烦,但它也会更灵活,因为呼叫者可以选择 属性 访问哪个。

    Future<int> getExampleIndexByUUID<T>({
      required T objectType,
      required String Function(T) getUuid,
      required String uuid,
    }) async {
      var box = await Hive.openBox<T>(objectDatabaseNameGetter(objectType));
      return box.values.toList().indexWhere(
        (example) => getUuid(example) == uuid),
      );
    }
    

    您可以进一步概括:

    Future<int> getExampleIndex<T, PropertyType>({
      required T objectType,
      required PropertyType Function(T) getProperty,
      required PropertyType propertyValue,
    }) async {
      var box = await Hive.openBox<T>(objectDatabaseNameGetter(objectType));
      return box.values.toList().indexWhere(
        (example) => getProperty(example) == propertyValue),
      );
    }
    
  • 使用duck-typing.

    如果您可以保证所有提供的类型都有一个 uuid 成员,另一种选择是使用 dynamic 和 duck-typing(放弃静态 type-safety):

    Future<int> getExampleIndexByUUID<T>({
      required T objectType,
      required String Function(T) getUuid,
      required String uuid,
    }) async {
      var box = await Hive.openBox<T>(objectDatabaseNameGetter(objectType));
      return box.values.toList().indexWhere(
        (dynamic example) => example.uuid == uuid),
      );
    }
    

顺便说一句,将 async/awaitFuture.then 混合使用是一种糟糕的风格。只需使用 async/await.