是否有一种语言支持的方法可以在Dart中制作对象的完整(深度)副本?
如果存在多个选项,它们有什么区别?
Dart 内置集合使用名为"from"的命名构造函数来实现此目的。请参阅此帖子: 克隆列表、地图或设置在飞镖
Map mapA = {
'foo': 'bar'
};
Map mapB = new Map.from(mapA);
就未解决的问题而言,似乎表明:
https://github.com/dart-lang/sdk/issues/3367
具体来说:
。对象具有标识,您只能传递对它们的引用。没有隐式复制。
晚了,但我最近遇到了这个问题,不得不做一些类似的事情:-
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);
我想对于不太复杂的对象,您可以使用转换库:
import 'dart:convert';
,然后使用 JSON 编码/解码功能
Map clonedObject = JSON.decode(JSON.encode(object));
如果您使用自定义类作为要克隆的对象中的值,则该类要么需要实现 toJson() 方法,要么必须为 JSON.encode 方法提供一个 toEncodable 函数,为解码调用提供一个 reviver 方法。
不幸的是没有语言支持。我所做的是创建一个名为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';
查看飞镖垫上的示例
https://dartpad.dev/f114ef18700a41a3aa04a4837c13c70e
参考@Phill 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);
假设你有一个类
Class DailyInfo
{
String xxx;
}
每天制作类对象的新克隆信息由
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());
尝试使用 Dart 提供的可复制界面。
这个问题有更简单的方法 只需使用...
运算符 例如,克隆地图
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 不会在多个线程中共享内存(隔离),因此...
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);
}
}
没有深度克隆对象的内置方法 - 您必须自己提供方法。
我经常需要从 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');
}
Dart 中没有内置用于克隆/深度复制的 API。
我们必须自己编写clone()
方法,(无论好坏)Dart作者都希望这样。
深拷贝对象/w 列表
如果我们要克隆的对象具有List
的对象作为字段,我们需要List.generate
该字段,并且这些对象需要自己的克隆方法。
在具有对象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]
. 这些都只做浅拷贝。
这对我有用。
在 JSON 序列化中使用对象类中的 fromJson 和 toJson
var copy = ObjectClass.fromJson(OrigObject.toJson());
dart 中的深度复制示例。
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;
}
创建一个帮助类:
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));
}
}
假设您要深度复制一个对象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));
}
接受的答案不提供答案,评分最高的答案对于更复杂的地图类型"不起作用"。
它也不会制作深拷贝,而是制作浅拷贝,这似乎是大多数人登陆此页面的方式。我的解决方案还制作了一个浅拷贝。
一些人建议的 JSON 克隆似乎只是浅层克隆的开销。
我基本上有这个
List <Map<String, dynamic>> source = [{'sampledata', []}];
List <Map<String, dynamic>> destination = [];
这有效,但当然,它不是克隆,它只是一个参考,但它在我的真实代码中证明了source
和destination
的数据类型是兼容的(在我的情况下相同,在这种情况下)。
destination[0] = source[0];
这不起作用
destination[0] = Map.from(source[0]);
这是简单的解决方案
destionation[0] = Map<String, dynamic>.from(source[0]);
Kotlin 风格的复制方法可以通过原型模式的实现来实现
为class LoginState{
String userName = "";
String password = "";
LoginState copy({String? userName, String? password}){
var loginState = LoginState();
loginState.userName = this.userName;
loginState.password = this.password;
if(userName != null) {
loginState.userName = userName;
}
if(password != null){
loginState.password = password;
}
return loginState;
}
}