How can I clone an Object (deep copy) in Dart?

是否有一种语言支持的方式,使一个完整的(深)副本的对象在省?

只是次要的; 有多种方法做到这一点,有什么区别?

谢谢你的澄清!

105719 次浏览

就公开的问题似乎表明的情况而言,答案是否定的:

https://github.com/dart-lang/sdk/issues/3367

特别是:

... 对象具有标识,并且只能传递对它们的引用。不存在隐式复制。

我想对于不太复杂的对象,您可以使用转换库:

import 'dart:convert';

然后使用 JSON 编码/解码功能

Map clonedObject = JSON.decode(JSON.encode(object));

如果使用自定义类作为对象中要克隆的值,那么该类要么需要实现 toJson ()方法,要么必须为 JSON.encode 方法提供 toEncodable 函数,为 decode 调用提供 revver 方法。

Dart 内置集合使用一个名为“ from”的命名构造函数来实现这一点

Map mapA = {
'foo': 'bar'
};
Map mapB = new Map.from(mapA);

参加聚会迟到了,但我最近遇到了这个问题,不得不采取一些措施,大致如下:-

class RandomObject {


RandomObject(this.x, this.y);


RandomObject.clone(RandomObject randomObject): this(randomObject.x, randomObject.y);


int x;
int y;
}

然后,你可以只调用复制与原件,如下所示:

final RandomObject original = RandomObject(1, 2);
final RandomObject copy = RandomObject.clone(original);

假设你有课

Class DailyInfo


{
String xxx;
}

创建类对象 dailyInfo 的新克隆

 DailyInfo newDailyInfo =  new DailyInfo.fromJson(dailyInfo.toJson());

为了使这个工作,您的类必须已经实现

 factory DailyInfo.fromJson(Map<String, dynamic> json) => _$DailyInfoFromJson(json);




Map<String, dynamic> toJson() => _$DailyInfoToJson(this);

这可以通过使类可序列化使用

@JsonSerializable(fieldRename: FieldRename.snake, includeIfNull: false)
Class DailyInfo{
String xxx;
}

它只适用于可以用 JSON 表示的对象类型。

ClassName newObj = ClassName.fromMap(obj.toMap());

或者

ClassName newObj = ClassName.fromJson(obj.toJson());

Trying using a 可复制 interface provided by Dart.

不幸的是,没有语言支持。我所做的是创建一个名为 Copyable的抽象类,我可以在我想要复制的类中实现它:

abstract class Copyable<T> {
T copy();
T copyWith();
}

然后我可以像下面这样使用它,例如,对于 Location 对象:

class Location implements Copyable<Location> {
Location({
required this.longitude,
required this.latitude,
required this.timestamp,
});


final double longitude;
final double latitude;
final DateTime timestamp;


@override
Location copy() => Location(
longitude: longitude,
latitude: latitude,
timestamp: timestamp,
);


@override
Location copyWith({
double? longitude,
double? latitude,
DateTime? timestamp,
}) =>
Location(
longitude: longitude ?? this.longitude,
latitude: latitude ?? this.latitude,
timestamp: timestamp ?? this.timestamp,
);
}

要在没有引用的情况下复制一个对象,我发现的解决方案与这里提到的类似,但是如果对象包含 MAP 或 LIST,你必须这样做:

class Item {
int id;
String nome;
String email;
bool logado;
Map mapa;
List lista;
Item({this.id, this.nome, this.email, this.logado, this.mapa, this.lista});


Item copyWith({ int id, String nome, String email, bool logado, Map mapa, List lista }) {
return Item(
id: id ?? this.id,
nome: nome ?? this.nome,
email: email ?? this.email,
logado: logado ?? this.logado,
mapa: mapa ?? Map.from(this.mapa ?? {}),
lista: lista ?? List.from(this.lista ?? []),
);
}
}
Item item1 = Item(
id: 1,
nome: 'João Silva',
email: 'joaosilva@gmail.com',
logado: true,
mapa: {
'chave1': 'valor1',
'chave2': 'valor2',
},
lista: ['1', '2'],
);


// -----------------
// copy and change data
Item item2 = item1.copyWith(
id: 2,
nome: 'Pedro de Nobrega',
lista: ['4', '5', '6', '7', '8']
);


// -----------------
// copy and not change data
Item item3 = item1.copyWith();


// -----------------
// copy and change a specific key of Map or List
Item item4 = item1.copyWith();
item4.mapa['chave2'] = 'valor2New';


在 dartpad 上可以看到一个例子

Https://dartpad.dev/f114ef18700a41a3aa04a4837c13c70e

省道中深拷贝的一个例子。

void main() {
Person person1 = Person(
id: 1001,
firstName: 'John',
lastName: 'Doe',
email: 'john.doe@email.com',
alive: true);


Person person2 = Person(
id: person1.id,
firstName: person1.firstName,
lastName: person1.lastName,
email: person1.email,
alive: person1.alive);


print('Object: person1');
print('id     : ${person1.id}');
print('fName  : ${person1.firstName}');
print('lName  : ${person1.lastName}');
print('email  : ${person1.email}');
print('alive  : ${person1.alive}');
print('=hashCode=: ${person1.hashCode}');


print('Object: person2');
print('id     : ${person2.id}');
print('fName  : ${person2.firstName}');
print('lName  : ${person2.lastName}');
print('email  : ${person2.email}');
print('alive  : ${person2.alive}');
print('=hashCode=: ${person2.hashCode}');
}


class Person {
int id;
String firstName;
String lastName;
String email;
bool alive;
Person({this.id, this.firstName, this.lastName, this.email, this.alive});
}

以及下面的输出。

id     : 1001
fName  : John
lName  : Doe
email  : john.doe@email.com
alive  : true
=hashCode=: 515186678


Object: person2
id     : 1001
fName  : John
lName  : Doe
email  : john.doe@email.com
alive  : true
=hashCode=: 686393765

希望能成功

 void main() {
List newList = [{"top": 179.399, "left": 384.5, "bottom": 362.6, "right": 1534.5}, {"top": 384.4, "left": 656.5, "bottom": 574.6, "right": 1264.5}];
List tempList = cloneMyList(newList);
tempList[0]["top"] = 100;
newList[1]["left"] = 300;
print(newList);
print(tempList);
}


List cloneMyList(List originalList) {
List clonedList = new List();
for(Map data in originalList) {
clonedList.add(Map.from(data));
}
return clonedList;
}

没有深度克隆对象的内置方法——您必须自己为它提供方法。

我经常需要从 JSON 对我的类进行编码/解码,所以我通常提供 MyClass fromMap(Map)Map<String, dynamic> toJson()方法。通过首先将对象编码为 JSON,然后再将其解码回来,可以使用它们来创建深度克隆。

但是,出于性能原因,我通常实现一个单独的 clone方法。虽然只有几分钟的工作时间,但我发现这段时间花得很值。

在下面的示例中,cloneSlow使用 JSON 技术,而 cloneFast使用显式实现的克隆方法。打印输出证明这个克隆实际上是一个深度克隆,而不仅仅是 a引用的副本。

import 'dart:convert';


class A{
String a;
A(this.a);
  

factory A.fromMap(Map map){
return A(
map['a']
);
}
  

Map<String, dynamic> toJson(){
return {
'a': a
};
}
  

  

A cloneSlow(){
return A.fromMap(jsonDecode(jsonEncode(this)));
}


A cloneFast(){
return A(
a
);
}
  

  

@override
String toString() => 'A(a: $a)';
}


void main() {
A a = A('a');
A b = a.cloneFast();
b.a = 'b';
  

print('a: $a   b: $b');
}








make a helper class:

class DeepCopy {
static clone(obj) {
var tempObj = {};
for (var key in obj.keys) {
tempObj[key] = obj[key];
}
return tempObj;
}
}

然后复制你想要的:

 List cloneList = [];
if (existList.length > 0) {
for (var element in existList) {
cloneList.add(DeepCopy.clone(element));
}
}

关于@Phil Wiggins 的回答,下面是一个带有. from 构造函数和命名参数的示例:

class SomeObject{
String parameter1;
String parameter2;


// Normal Constructor
SomeObject({
this.parameter1,
this.parameter2,
});


// .from Constructor for copying
factory SomeObject.from(SomeObject objectA){
return SomeObject(
parameter1: objectA.parameter1,
parameter2: objectA.parameter2,
);
}


}

然后,在你想复制的地方这样做:

SomeObject a = SomeObject(parameter1: "param1", parameter2: "param2");
SomeObject copyOfA = SomeObject.from(a);

比方说,您希望深度复制一个对象 Person,该对象具有一个属性,该属性是其他对象 Skills的列表。按照惯例,我们使用带有可选参数的 copyWith方法进行深度复制,但是您可以为它命名任何您想要的名称。

你可以这么做

class Skills {
final String name;


Skills({required this.name});


Skills copyWith({
String? name,
}) {
return Skills(
name: name ?? this.name,
);
}
}


class Person {
final List<Skills> skills;


const Person({required this.skills});


Person copyWith({
List<Skills>? skills,
}) =>
Person(skills: skills ?? this.skills.map((e) => e.copyWith()).toList());
}

请记住,仅使用 this.skills只会复制列表的引用。所以原来的物体和复制的物体会指向相同的技能列表。

  Person copyWith({
List<Skills>? skills,
}) =>
Person(skills: skills ?? this.skills);

如果您的列表是基元类型,您可以这样做。基本类型会被自动复制,因此您可以使用这种更短的语法。

class Person {
final List<int> names;


const Person({required this.names});


Person copyWith({
List<int>? names,
}) =>
Person(names: names ?? []...addAll(names));
}

解决这个问题有更简单的方法 只要使用 ...操作员 例如,克隆一个 Map

Map p = {'name' : 'parsa','age' : 27};
Map n = {...p};

还可以对类属性执行此操作。 在我的例子中,需要克隆一个类的列表属性。 所以:

class P1 {
List<String> names = [some data];
}


/// codes
P1 p = P1();
List<String> clonedList = [...p.names]
// now clonedList is an unreferenced type

Dart 内置没有用于克隆/深度复制的 API。

我们必须 自己编写 clone()方法 & (或好或坏)的达特作者希望它的方式。

深度拷贝对象/w 列表

如果我们要克隆的 Object 有一个 List的 Objects 作为字段,我们需要 List.generate这个字段,那些 Objects 需要它们自己的克隆方法。

在具有 List对象字段的 Order类上克隆方法(copyWith())的示例(并且那些嵌套对象也具有 copyWith()) :

  Order copyWith({
int? id,
Customer? customer,
List<OrderItem>? items,
}) {
return Order(
id: id ?? this.id,
customer: customer ?? this.customer,
//items: items ?? this.items, // this will NOT work, it references
items: items ?? List.generate(this.items.length, (i) => this.items[i].copyWith()),
);
}

甘特提到了这个 给你

注意,我们 不能使用 List.from(items)也不使用 [...items]。这两者都只使 shallow copies

接受的答案没有提供答案,评分最高的答案对于更复杂的 Map 类型“不起作用”。

It also doesn't make a deep copy, it makes a shallow copy which seems to be how most people land on this page. My solution also makes a shallow copy.

少数人建议使用 JSON 克隆,这对于一个浅克隆来说似乎只是一笔开销。

基本上就是这样

List <Map<String, dynamic>> source = [{'sampledata', []}];
List <Map<String, dynamic>> destination = [];

这是有效的,但是当然,它不是克隆的,它只是一个参考,但是它在我的实际代码中证明了 sourcedestination的数据类型是兼容的(在我的例子中是相同的,在这个例子中也是相同的)。

destination[0] = source[0];

这招不管用

destination[0] = Map.from(source[0]);

这是最简单的解决办法

destionation[0] = Map<String, dynamic>.from(source[0]);

Dart 不在多个线程中共享内存(隔离) ,所以..。

extension Clone<T> on T {
  

/// in Flutter
Future<T> clone() => compute<T, T>((e) => e, this);


/// in Dart
Future<T> clone() async {
final receive = ReceivePort();
receive.sendPort.send(this);
return receive.first.then((e) => e as T).whenComplete(receive.close);
}
}


This works for me.

在 JSON 序列化中使用对象类中的 from JSON 和 toJson

var copy = ObjectClass.fromJson(OrigObject.toJson());