Notifications
Clear all

[Closed] Copying dotNetObject to unique instance?

I always just assumed this worked, but I guess never actually tried before. Is there a way to create a copy of dotNetObject so that the copy is a unique instance?

Basically, I have one object that is going to be created 100 times. Instead of running the creation code 100 times, since the overhead might be a little much, I figured I’d just run it once, create copies and then modify certain properties on the copies. However, the code below is telling me I am crazy to think this way:

-- works as expected
test = box()
test2 = copy test
test == test2

-- does not work as expected
test = dotNetObject "label"
test2 = copy test
test == test2

Is there some trick to copying dotNetObjects to unique instances? Can it be done at all?

9 Replies
1 Reply
(@denist)
Joined: 10 months ago

Posts: 0

unfortunately the cloning of object which type doesn’t have IClonable interface implemented is not easy.
but in your case the code:


fn CloneDotnetObject obj = if iskindof obj dotnetobject do
(
	t = obj.GetType()
	properties = t.GetProperties()

	new = t.InvokeMember "" (dotnetclass "System.Reflection.BindingFlags").CreateInstance t.DefaultBinder obj #()

	for p in properties where p.CanWrite do p.SetValue new (p.GetValue obj #()) #()
	new
)

… might to work. The function doesn’t make a deep copy.

Does anybody know if there is a way to go about this?

the maxscript copy function does not work on dotnetobjects. your best bet is to create a function with arguments of the parameters you need to change.
eg.

-- cache the button class for speed
  ButtonClass = dotnetclass "System.Windows.Forms.Button"
 
  fn DotNetButton width height txt =
  (
  	b = dotnetobject ButtonClass
  	-- common properties
  	b.BorderStyle = b.BorderStyle.PopUp
  	b.BackColor = b.BackColor.FromARGB 100 150 200
  	-- unique properties
  	b.Width = width
  	b.Height = height
  	b.Text = txt
  	-- return the button
  	b
  )
 PEN

From what I can tell it is not possible to copy a form object, only something like a treeViewNode can be done using iCloneable in dotNet. We will just have to rebuild them as needed.

dotNetObject "system.iCloneable" treeViewNode

Hi,

You have to rely on provided Copy/Clone methods on the type itself, if there are any. Some types implement ICloneable, but the it’s ambiguous for the type of method used for copying (shallow vs deep).

.NET also doesn’t provide a built-in DeepCopy that could work on any type. There are a couple ways to do this, of which this one is commonly used:

	local ms = DotNetObject "System.IO.MemoryStream"
   	local bf = DotNetObject "System.Runtime.Serialization.Formatters.Binary.BinaryFormatter"
   	
   	bf.Serialize ms test
   	ms.Position = 0
   	local test2 = bf.Deserialize ms
   	
   	ms.Close()

which in your case wouldn’t work, because Label type isn’t Serializable. (:

I prefer not to use these kinds of generic copy methods, because they are slow. So the best way is to provide your own copy methods for your own types or extend the existing types to provide these methods for them.

But even if you do, you still have to call the constructor or do the same amount of work which might defeat your purpose.

Sorry not a very helpful reply, but still…

Light

 PEN

Thanks guys. Denis I’m not sure what the line is doing that starts with new=. Mat and I will have a look at that deeper today if we can. I guess we will have to check what is faster, just rebuilding the objects or trying to make a copy.

Thanks for the help.

Clever Denis! I like the property set(get())

You can also use the System.Activator to call the default constructor. If you do then pass asdotnetobject:true in case mxs wants to convert the returned object. Primitive types don’t have properties, so I test for them.

fn CloneDotnetObject obj = if iskindof obj dotnetobject do
(
	t = obj.GetType()
	if (t.isprimitive) then
	(
		clone = obj	
	)
	else
	(
		clone = (dotnetclass "Activator").CreateInstance t true asdotnetobject:true		
		properties = t.GetProperties()
		for p in properties where p.CanWrite do p.SetValue clone (p.GetValue obj #()) #()
	)	
	clone
)



[Paul, just replace “new” with “clone” and it might be clearer. It’s the ‘new’ object that is returned when the default constructor function is called]

2 Replies
(@denist)
Joined: 10 months ago

Posts: 0

you are absolutely right. Activator.CreateInstance is definitely more succinct and probably faster.

 PEN
(@pen)
Joined: 10 months ago

Posts: 0

Thanks Mike, that looks good. Still not sure that it is saving us anything as we have nested labels that need to be copied at the same time. This could be faster how ever recursing this way then just rebuilding.

Thanks.