Cloning Objects - Introspection

I want to be able to clone an object, for now I just need to clone the public properties although I may want to be able to clone private state too. I am thinking that a copy constructor will work well enough and I know that I can ‘hard code’ the assignment of the properties but I was thinking that this would be better done by Introspection. I should be able to get the Properties of the clone and assign them from the source object but I just can’t work out how it should happen in Xojo. Can anyone send me in the right direction?

Essentially I have two objects of the same Class, I want to iterate through the properties of one object and assign the property value to the other object using Introspection - without hard coding the assignment. So I don’t want to use:

obj2.Name = obj1.Name

I want to write something like

dim pObj1() as Introspection.PropertyInfo = Introspection.GetType(obj1).GetProperties()

for each p as Introspection.PropertyInfo in pObj1
p.Value(obj2) = p.Value(obj1)
next

However it doesn’t seem to be that simple - my app blows up on the first iteration!

Just use a Clone method. In the method, create a new instance and copy the values you need. You can read private and protected values of another instance of the same class.

Thanks Thom, I would prefer an Introspection approach though (if possible) so that I do not have to maintain the cloning whenever I add/change properties.

Using Introspection you wont know if its broken until runtime.
And unless you have 100% code coverage for tests its a silent bug waiting to happen.
Doing it in a clone method you can copy things and the compiler will even help you out where it can.

And it’s not like you write “clone” every day for every class.

In the entire IDE we have a very tiny number of classes that need “clone”

Thanks for your advice, two influential Xojo users must have a point based on experience, but I still want to do it. Norman the point that you make about runtime bugs is obviously valid, I have an exception handler that allows escape from the potential problem but obviously does not really fix the problem since the cloning will have failed. The opposing argument is that it would be just as buggy if I have to maintain any individual Clone() methods and ‘forget’ to clone some new properties. I think that I will end up with a hybrid approach where I look for a Clone() method on each object and use that if I find it rather than calling my Clone() extension method, that way a class can override the behaviour when necessary.

I almost have it working in 15 lines of code using an extension method called, tada! Clone(). It barfs at my dictionary subclasses but seems ok for the others walking through their properties and if the property is a class it recursively calls itself. So I can write

dim original as new Foo()

// do something that changes original's state

dim copy as Foo()  
copy = Foo(original.Clone())

BTW It would be nice if the code completion in the IDE could show extension methods from super classes too and another ‘nice to have’ with code completion is for it to ignore the return value of the method and show all methods. ATM if a method has a return value then you need to type:

xyz = object.[TAB] to see it - so if you are just looking for a method but don’t know what it is and you type:

object.[TAB] you will only see the methods that have no return value. Personally I would rather see all of the methods and have the compiler warn me that I need to use the return value later rather than not see all of the methods

Anyway, I get more impressed with Xojo as I learn more, sure there are a few annoyances (for me) but overall it is very positive and gets better as I discover more. I really like extension methods, they are a powerful way to reduce code and help with readability, Generics would be nice to have one day for similar reasons.

The next plan is to write Serialise() and Deserialise() extension methods so I can save and restore state to some persistent storage and so my ‘discoveries’ with Introspection and Clone() will help there too.

Xojo should have this natively. That’s very useful. Something like:

Dim o1 as new myObject(11, “teste”)
Dim s as String = o1.Serialize()
Dim o2 as object()
o2.Deserialize(s) // o2 is a clone of o1, if I transmit s over the network I can recreate that object on the other side

Yes, useful for transmission and also useful if you have an object model for your ‘documents’ that you can save and load from files and databases. If it was in the Xojo framework then it should support binary serialisation (faster for transmission) and XML + JSON serialisation (better for open standards and managing revision of the object model). Without custom attributes on properties it will not be possible to control the serialisation declaratively. It does look like it is do-able though but I have not tried it out yet so I may hit some blocking issues.

Is there a good example for this?

I have an object with more then 400 properties and want to clone all properties easily to another object.

[quote=93382:@Christoph De Vocht]Is there a good example for this?

I have an object with more then 400 properties and want to clone all properties easily to another object.[/quote]

Function Clone() As yourClass
  dim tmp as new yourClass
  tmp.backgroundColor = backgroundColor
  tmp.bold = bold
  tmp.hasBackgroundColor = hasBackgroundColor
  tmp.italic = italic
  tmp.lastFont = lastFont
  tmp.lastSize = lastSize
  tmp.textColor = textColor
  tmp.Type = TYPE
  tmp.underline = underline
  tmp.width = width
  tmp.offset = offset
  tmp.length = length
  
  Return tmp
End Function

Would the code above be of any help? Use that idea of simply creating a new instance of that class assigning properties of that new instance to the original instance and then returning the cloned class.

I hope everything else is self-explanatory for you and this is what you where looking for.

Maybe you want to clone an array also. That can be done with something like this:

Function Clone() As yourClass
  dim tmp as new yourClass

for each item as arrayItem in yourArray
  tmp.yourArray.append item
next

  Return tmp
End Function

[quote=93428:@Oliver Scott-Brown]Function Clone() As yourClass
dim tmp as new yourClass
tmp.backgroundColor = backgroundColor
tmp.bold = bold
tmp.hasBackgroundColor = hasBackgroundColor
tmp.italic = italic
tmp.lastFont = lastFont
tmp.lastSize = lastSize
tmp.textColor = textColor
tmp.Type = TYPE
tmp.underline = underline
tmp.width = width
tmp.offset = offset
tmp.length = length

Return tmp
End Function
Would the code above be of any help? Use that idea of simply creating a new instance of that class assigning properties of that new instance to the original instance and then returning the cloned class.[/quote]

Thanks for the input.
This is how I do it now. But as said myClass has over 400 properties (and I have to add new one on regular basis).
So you can guess it takes some time to add them. I was more or less thinking if there is a shorter way doing this.

It would be great if it was possible to go thru all the properties with some sort of for each … loop
So no matter how many properties I add, it will always work.

[quote=93436:@Christoph De Vocht]Thanks for the input.
This is how I do it now. But as said myClass has over 400 properties (and I have to add new one on regular basis).
So you can guess it takes some time to add them. I was more or less thinking if there is a shorter way doing this.

It would be great if it was possible to go thru all the properties with some sort of for each … loop
So no matter how many properties I add, it will always work.[/quote]
Oh sorry. I see. I think it would be cool if someone could write a buildscript or something, and have a checkedlistbox to choose which properties are copied. I don’t have any knowledge really of buildscripts though.

I am guessing I am telling you stuff you already know.