如何在Dart中克隆对象(深层复制)?

时间:2012-10-28 10:03:32

标签: object copy clone dart

是否支持语言在Dart中制作对象的完整(深层)副本?

仅限中学;有多种方法可以做到这一点,有什么区别?

感谢您的澄清!

17 个答案:

答案 0 :(得分:13)

没有公开的问题似乎表明:

http://code.google.com/p/dart/issues/detail?id=3367

具体而言:

.. Objects have identity, and you can only pass around references to them. There is no implicit copying.

答案 1 :(得分:6)

Darts内置集合使用名为“from”的命名构造函数来完成此任务。请参阅此帖子:Clone a List, Map or Set in Dart

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

答案 2 :(得分:4)

要复制一个没有引用的对象,我发现的解决方案与此处发布的解决方案相似,但是如果该对象包含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

答案 3 :(得分:3)

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

import 'dart:convert';

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

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

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

答案 4 :(得分:3)

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

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

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

答案 5 :(得分:3)

尝试使用Dart提供的Copyable界面。

答案 6 :(得分:3)

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

我经常需要从 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');
}




答案 7 :(得分:2)

很遗憾,没有语言支持。我要做的是创建一个名为copyable的抽象类,可以在我想复制的类中实现:

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

然后我可以按以下方式使用它,例如位置对象:

@override
Location copy() {
  return Location(
      longitude: this.longitude,
      latitude: this.latitude,
      timestamp: this.timestamp,
      country: this.country,
      locality: this.locality,
      postalCode: this.postalCode,
      name: this.name,
      isoCountryCode: this.isoCountryCode,
      bearing: this.bearing);
}

@override
Location copyWith({
    String country,
    String locality,
    String name
  }) {
  return Location(
      country: country,
      locality: locality,
      name: name,
      longitude: this.longitude,
      latitude: this.latitude,
      timestamp: this.timestamp,
      postalCode: this.postalCode,
      isoCountryCode: this.isoCountryCode,
      bearing: this.bearing);
}

答案 8 :(得分:0)

比方说,您想要深度复制一个对象 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));
}

答案 9 :(得分:0)

很多答案都说你可以定义一个新的构造函数来启用深度复制,但 CONSIDER copying nullable field to enable type promotion 下的 Effective Dart Usage Guide 提供了一个更简单的解决方案。

文档建议使用局部变量来制作可用的副本。从标题可以推断出,如果可空字段有值,此技术还会将它们转换为不可空字段。

示例代码:

class UploadException {
  final Response? response;

  UploadException([this.response]);

  @override
  String toString() {
    var response = this.response; // This makes a new copy.
    if (response != null) {
      return "Could not complete upload to ${response.url} "
          "(error code ${response.errorCode}): ${response.reason}.";
    }

    return "Could not upload (no response).";
  }
}

注意:

如果在整个代码库中广泛使用,那么围绕此局部变量的便利构造函数包装器将更合适。我认为这种技术更简洁,因为它减少了编写具有相同最终结果的代码量。

注意事项:

使用本地副本时要小心。如果您需要写回该字段,请确保这样做,而不要只写入局部变量。此外,如果该字段可能在本地仍在范围内时发生更改,则本地可能具有过时的值。有时最好在场上简单地使用 !

答案 10 :(得分:0)

创建一个辅助类:

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));
    }
 }

答案 11 :(得分:0)

//希望这项工作

 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;
}

答案 12 :(得分:0)

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

答案 13 :(得分:0)

假设您有课

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;
}

答案 14 :(得分:0)

晚了聚会,但是我最近遇到了这个问题,不得不采取以下措施:-

RandomObject {

int x;
int y;

RandomObject(int x, int y) {
    this.x = x;
    this.y = y;
}

RandomObject.clone(RandomObject randomObject): super(randomObject.x, randomObject.y);
}

调用RandomObject.clone似乎做了类似的事情。可以将其添加到抽象类中,以便您可以创建对象的通用生成器。

答案 15 :(得分:-1)

参考@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);

答案 16 :(得分:-3)

您可以使用可相等的库https://pub.dev/packages/equatable 如果它们扩展了Equatable类,则允许等同于同一类的不同实例。