【发布时间】:2021-03-26 23:07:27
【问题描述】:
我试过List.from 和[...orginalObject],但也没有深拷贝。在这两种情况下,它显然只是复制参考。这意味着如果我更改复制数据的列表中的任何数据,原始数据也会更改。
在我看来,执行for 循环并使用new 运算符定义复制列表中的每个条目以及来自原始列表中每个相应条目的数据的唯一方法。如下图所示。
似乎是一种相当乏味的方法。有没有更简单的方法?
谢谢。
【问题讨论】:
我试过List.from 和[...orginalObject],但也没有深拷贝。在这两种情况下,它显然只是复制参考。这意味着如果我更改复制数据的列表中的任何数据,原始数据也会更改。
在我看来,执行for 循环并使用new 运算符定义复制列表中的每个条目以及来自原始列表中每个相应条目的数据的唯一方法。如下图所示。
似乎是一种相当乏味的方法。有没有更简单的方法?
谢谢。
【问题讨论】:
在 vanilla Dart 中,并非如此,因为这需要复制叶对象的能力,而语言无法知道如何对任意对象执行此操作。
但是,built_value 和 built_collection 软件包可能会解决您的问题。它们的主要特征之一是深度不变性。首先,你这样写你的类:
class Item {
final String foo;
final int bar;
Item(this.foo, this.bar);
}
// becomes
part 'item.g.dart';
abstract class Item extends Built<Item, ItemBuilder> {
Item._();
factory Item([void Function(ItemBuilder) updates]) = _$Item;
// boilerplate, can use tooling to auto-generate
String get foo;
int get bar;
}
这使您可以使用可变 Builder 创建一个不可变对象,然后将其最终化,例如:
final item1 = Item((b) {
// here b is an ItemBuilder, the mutable version of Item
b.foo = 'Hello';
b.bar = 123;
});
final item2 = item1.rebuild((b) => b.foo = 'New Value'); // Item(foo: 'New Value', bar: 123)
然后,您可以使用built_collection 处理构建值的深度不可变集合,例如:
final list = BuiltList<Item>([item1, item2, item3]);
final deepCopy = list.rebuild((b) {}); // rebuild with no changes
final withNewElement = list.rebuild((b) => b.add(item4));
所有这些的重要一点是它们都是不可变的,并且是“值对象”,这意味着(除其他外)如果它们所有对应的值都相等,则它们被认为是相等的。您可以认为每个 rebuild 调用都将返回一个全新的列表,其中包含所有已复制的值。
事实上,这个库足够聪明,可以只为实际发生变化的值创建新对象,但由于它是不可变的,它就像一个深拷贝,没有潜在的性能成本。
这些库的主要缺点是它们依赖于代码生成,因此需要进行更多设置,而不仅仅是将其放入您的 dependencies。作为回报,除了具有深度不可变对象之外,您还可以获得许多有用的功能(例如 json 序列化、在预空安全代码中进行空检查、@memoized 用于缓存昂贵计算的结果等)
【讨论】:
Dart 没有复制构造函数,也不需要对象上的clone() 方法,因此无法复制任意对象,因此无法对任意对象的集合进行深度复制。
cameron1024's answer 建议使用package:built_value 和package:built_collection 很好。但是,如果它们不适合您,您可以创建一个通过回调复制元素的函数:
List<T> copyList<T>(Iterable<T> items, T Function(T element) copier) {
return [
for (var item in items) copier(item),
];
}
class Foo {
int i;
String s;
Foo(this.i, this.s);
@override
String toString() => 'Foo($i, "$s")';
}
void main() {
var list = [Foo(1, 'one'), Foo(2, 'two'), Foo(3, 'three')];
print('Original: $list');
var copy = copyList<Foo>(list, (foo) => Foo(foo.i, foo.s));
list[0].i = 100;
print('Mutated: $list');
print('Copy: $copy');
}
【讨论】: