【问题标题】:Generic method that returns List<T> instead returns List<dynamic>返回 List<T> 的通用方法改为返回 List<dynamic>
【发布时间】:2022-07-21 21:20:37
【问题描述】:

我尝试编写一个简单的泛型方法来迭代复制嵌套的List,例如List&lt;List&lt;int&gt;&gt;。但不幸的是,递归调用似乎总是返回List&lt;dynamic&gt;,所以我得到以下错误

The argument type List<dynamic> can't be assigned to the parameter type T
List<T> listDeepCopy<T>(List<T> list){
  List<T> newList = List<T>();

  list.forEach((value) {
    if( value is List ){
      newList.add(listDeepCopy(value));  // <-- listDeepCopy() always returns List<dynamic>
    }
    else{
      newList.add(value);      
    }
  });

  return newList;
}

所以如果我打电话

List<List<int>> list = [[1,2],[3,4]];
List<List<int>> copy = listDeepCopy(list);

TList&lt;int&gt;

valueT - 即List&lt;int&gt;

listDeepCopy(value) 应该等于listDeepCopy&lt;List&lt;int&gt;&gt;,这将返回一个List&lt;int&gt;,它应该可以添加到newList,这是一个List&lt;List&lt;int&gt;&gt;

我在哪里出错了,我怎样才能使这样的事情起作用?

【问题讨论】:

    标签: dart generics


    【解决方案1】:

    我可能会将其实现为:

    List<T> listDeepCopy<T>(List<T> list) {
      var copy = list.toList();
      for (var i = 0; i < copy.length; i += 1) {
        var element = copy[i];
        if (element is List) {
          copy[i] = listDeepCopy(element) as T;
        }
      }
      return copy;
    }
    
    void main() {
      List<List<int>> list = [
        [1, 2],
        [3, 4]
      ];
      List<List<int>> copy = listDeepCopy(list);
    
      list[0][0] = 99;
      print(copy); // Prints: [[1, 2], [3, 4]]
    }
    

    您的方法的一个问题是 Dart 无法正确推断该递归 listDeepCopy(value) 调用的泛型类型参数。 valueT 类型,已知是 List(这是 List&lt;dynamic&gt; 的简写),我不知道提取静态元素类型的方法。 (也许@lrn 会看到这一点并提供更好、更完整的解释。)

    在这种情况下,最好通过调用 List 上的方法来依赖多态性,该方法返回自身的副本:.toList()

    (作为一个重要的例子,考虑一个浅拷贝场景:

    List<T> shallowCopy1<T>(List<T> list) => <T>[...list];
    
    List<T> shallowCopy2<T>(List<T> list) => list.toList();
    
    extension StaticType<T> on T {
      Type get staticType => T;
    }
    
    void main() {
      List<num> list = <int>[1, 2, 3];
      var copy1 = shallowCopy1(list);
      var copy2 = shallowCopy2(list);
    
      print('original: staticType: ${list.staticType}, runtimeType: ${list.runtimeType}');
      print('copy1: staticType: ${copy1.staticType}, runtimeType: ${copy1.runtimeType}');
      print('copy2: staticType: ${copy2.staticType}, runtimeType: ${copy2.runtimeType}');
    }
    

    虽然两个副本都保留了原始List静态 类型,但只有copy2 保留了对象的实际(运行时)类型。正确的副本取决于被复制对象的 runtime 类型,唯一可靠的方法是让对象创建自身的副本。)

    【讨论】:

    • 谢谢,您的解决方案似乎对ListSet 都有效!现在,如果只有一种方法可以为Map 实现相同的目标...但从我目前发现的情况来看,这似乎是不可能的(对于嵌套的Maps)?
    • @Magnus 是的,我想不出为Maps 做这件事的方法,因为Map 没有提供一种方法来制作自身的浅拷贝而不依赖于静态类型.
    【解决方案2】:

    你不能按照你想要的方式去做。

    问题在于deepClone&lt;T&gt;List&lt;dynamic&gt; 转换为List&lt;T&gt;(这很好),然后尝试将本身是列表的元素转换为类型化列表......但您不知道类型。 实际上,当您检查 value is List 时,您不知道要将其转换为哪种列表。 有两种情况:

    • TList&lt;X&gt;Iterable&lt;X&gt; 对于某些类型的X,但您无法获得X。 Dart 不允许您在运行时解构类型。
    • TObject 或另一个没有“列表元素”类型的通用超类型,然后您根本没有任何关于将嵌套列表转换为List 类型的信息。 (这实际上是最简单的情况,因为您根本不应该deepClone 列表)。

    有一种方法可以确定您处于哪种情况 (&lt;T&gt;[] is List&lt;Iterable&lt;Object?&gt;&gt;),但在前一种情况下它对您没有帮助,除非您想对X 的所有可能类型进行详尽的搜索可能是。

    我要做的是构建一个转换器,而不是使用单个函数。

    abstract class Cloner<T> {
      const factory Cloner() = _ValueCloner<T>;
      T clone(dynamic source);
      Cloner<List<T>> get list => _ListCloner(this);
    }
    abstract class _BaseCloner<T> implements Cloner<T> {
      const _BaseCloner();
      Cloner<List<T>> get list => _ListCloner<T>(this);
    }
    class _ValueCloner<T> extends _BaseCloner<T> {
      const _ValueCloner();
      T clone(dynamic source) => source as T;
    }
    class _ListCloner<T> extends _BaseCloner<List<T>> {
      final Cloner<T> _base;
      _ListCloner(this._base);
      List<T> clone(dynamic source) =>
         <T>[for (var o in source as List<dynamic>) _base.clone(o)];
    }
    

    然后,如果您确实知道数据的类型,则可以将克隆器构建为:

    var typedList =
        Cloner<int>().list.list.clone(
            <dynamic>[<dynamic>[1, 2], <dynamic>[3, 4]]);
    

    产生一个List&lt;List&lt;int&gt;&gt;,其值为&lt;List&lt;int&gt;&gt;[&lt;int&gt;[1, 2], &lt;int&gt;[3, 4]]

    【讨论】:

      猜你喜欢
      • 2019-02-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多