-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add clone() method to Object class to create an independent copy instance of any object #45033
Comments
/cc @lrhn |
Not great, but the isolate library can be misused to implement this: import 'dart:isolate';
extension Clone<T> on T {
Future<T> clone() {
final receive = ReceivePort();
receive.sendPort.send(this);
return receive.first.then((e) => e as T).whenComplete(receive.close);
}
} If we end up having blocking receive ports (#44804), this wouldn't have to be async either. |
It's not something we generally want (and the isolate approach is something I'm not too happy with to begin with). Some classes can have invariants which are not entirely contained inside the object. Perhaps a class registers all new instances created in a global registry, or with an expando. The cloned object fails this invariant. The class would have to be aware of this and override Some classes might be depending on a private component not being shared with anyone. If the cloning is shallow, that will stop being the case. Shallow-cloning a growable list would create two growable lists sharing the same backing store. Again, the class would have to be aware of this and override Similarly, some classes may be identity based, and doing a deep clone would be bad for them. A deep clone of an identity-based hash set would change the hash codes of the cloned elements, but not the integer hash codes already stored in the data-structure, making look-up fail. And again, the class would have to be aware of it and override Too many things could go wring if we just added That suggests that we should instead make it an opt-in operation. In Java, you have Dart could also add a |
Adding my junior-to-Dart point of view, I am wondering first why do people need to clone objects, and maybe it's the same reason for 90% of cases. I imagine that Dart is used mainly in Flutter, which is a declarative UI library, as React. With that comes states managements tools that makes your widgets reload when their picked data changes. So you may see where I'm coming, maybe the real solution to the issue is to have another type of classes (data classes, structs), which could hold only other data classes / primitives, and nether have logic (?). The clone operation would be possible I guess. I know there must be issues on that data classes thing. So my point is : Why do people want to clone stuff ? For me, it's to "update" anemic objects, and not rich models or any other class. |
Maybe to filter a list of objects without losing the initial instance? |
Cloning a list is trivial, just do Cloning a more complicated object with private fields and global state depending on the object is a much different thing. Allowing you to clone such an object anyway, from the outside, risks creating an object that doesn't actually work (cloning an identity based So, in short, if you want to clone an object, and that object and it's class doesn't give you a way to do so, maybe you shouldn't be doing it. |
Cause sometimes backend developers make data structures as s**t and for that we need to do so many stupid things. And when we ask to change the structure they won't they'll ask you to manage |
My reason for cloning an object is that I want to have an edit screen where I can edit the fields of an object, and that edit screen should only display the "Save" and "Reset" buttons if the Object differs from the original object. For that reason, I'll have some logic along the lines of:
In this case, the object naturally has an equals method that evaluates to true if all the fields have equal values. This has the result that the buttons appear as soon as a field is changed, and disappear again if you manually change them back to the original value. The extended application for that is naturally that a file can display when it has changed and needs saving on a "by content" basis, as opposed to simply setting a flag as soon as the first change is made. |
You can get help from the compute function ** Not Recommended ** though.
|
That's effectively sending the object through an isolate communication port. You don't need an extra isolate for it, you can send and receive in the same isolate: import "dart:isolate";
Future<T> clone<T>(T value) async {
var port = ReceivePort();
port.sendPort.send(value);
return (await port.first) as T;
} But I really do not recommend doing it, because
Any number of things can go wrong when copying objects. If the object has relations to static data around it, that state might not recognize the new copy. You may end up with two objects with identical "unique" IDs. And if you do it anyway, and things crash, remember that you asked for it :) |
I think the abstract class Cloneable {
Self clone();
} |
You can get far with F-bounded polymorphism: abstract interface class Clonable<T extends Clonable<T>> {
T clone();
} Then you can have: class Point implements Clonable<Point> {
final double x, y;
Point(this.x, this.y);
Point rotate(double by) {
var s = sin(by);
var c = cos(by);
return Point(x * c - y * s, x * s + y * c);
}
Point clone() => Point(x, y);
String toString() => "${(x, y)}";
} and a private helper class that is a class _PolarPoint implements Point {
double _arg, _mag;
factory _PolarPoint(double arg, double magnitude) : _arg = arg, _mag = magnitude;
// Cheap rotate.
Point rotate(double by) => _PolarPoint((_arg + by) % (pi * 2), _mag);
// Expensive Cartesian coordinates.
double get x => cos(_arg) * _mag;
double get y => sin(_arg) * _mag;
Point clone() => Point(x, y); // Valid clone of Clonable<Point>.
String toString() => "${(x, y)}";
} If you don't use self-types, then the type of clone can be a supertype (well, or any other type for that matter, but that gets weird fast). Using a supertype makes sense for private subtypes used internally by a library, that may want to be something else if cloned. You don't even need to F-bounded polymorphism, it can just be |
I know this has been asked multiple times already (#3367, #35309), but since this is not just about simple classes, but also about methods, and developers run into this issue quite often, I wanted to elaborate on this a little bit.
Creating independent copies of objects can be somewhat cumbersome, because objects (except for literals) are usually passed by reference.
I don't want to create custom classes extending basic classes like
List
orMap
, and then create a dedicatedclone()
method for/in every single one of the classes I use and create myself.I also don't want to use JsonSerializable and
Foo.fromJson(jsonDecode(jsonEncode(obj)))
everywhere, since that a is a rediculessly heavy load, especially for large objects, just to create a copy. Plus, I don't even know if this works flawlessly with rather complex objects.Shouldn't it be possible to add a
clone()
method to theObject
class, which creates an independent instance of that object, without properties referencing each other?I am a Flutter developer, so I don't know how deeply into the OS and on the device Dart can go, whether it is able or not to work on the RAM as languages like C or C++ can. But I figured, a low level copy on the RAM might actually be the most efficient way, rather than trying some complicated recursive way on the "Dart language" layer.
The text was updated successfully, but these errors were encountered: