3 hours ago, lawnjelly said:
Thanks Nypyren, I have read that using a 'struct' may be the answer to creating what would be considered normal programming code, I haven't tried it yet.
I just spent 3 hours debugging yet another issue caused by these damn references. I have an array of a small class, and I wanted to copy one element to another, naturally I used a[x] = a[x+1], or similar code.
This DOESN'T copy the element, as you would expect, it seems to copy the reference. WTF happens to the object that was there in a[x] I don't know, it possibly disappears into one of stephen hawkings' black holes, or into a secret missing sock stash. I spent ages trying to figure out if it was some bizarre multithreading bug, but no, C# doesn't actually seem to do basic language expressions.
Instead I had to write a copy function into the class, in order to copy the object members (that weren't references) one by one, just to copy the F***ing data. Is there some kind of equivalent of memcopy that it's possible to use to copy data from an object to another?
This isn't helped by the fact that my monodevelop install doesn't seem to want to debug with unity, so I can't see what's going on in the memory, and am relying on hundreds of verbose Debug.Logging statements.
This language is hands down the worst I've ever used in 35 years.
If you find yourself wanting to copy, that's when you should use structs. The assignment operator for structs copies every single field just like you'd get with a memcpy. HOWEVER, if a member of a struct is a class type, that field will have just the pointer copied.
On the other hand, every single "class" uses reference semantics: copying JUST the pointer, not the object being pointed at. It's just like every variable which is a 'class' type also has a C++ * type qualifier on its declaration. The only ways to copy the contents are to manually copy them, or use a struct instead.
Sometimes you don't want to copy a struct. In those cases you can pass "pointers" to structs in two main ways: Make the struct the member of a class and pass the class, or use the 'ref' keyword. The ref keyword is basically the & type qualifier from C++, but can only be used in function argument lists.
One of the major things you should beware of with structs is combining them with properties. A property works like a method, so a property for a struct will return a COPY of a struct.
Collections (arrays, lists, etc) are classes which contain whatever type you declare them with, and the elements use their type's semantics - structs in an array will use copy semantics (assigning a[x] = a[x+1] will copy the whole struct, NOT just a reference). Any class types as an element of an array will make a pointer copy as you've discovered.
Arrays themselves are classes, so you cannot copy the contents of an array to another array using the = operator. You have to either loop-and-copy-elements or use Array.Copy functions. Arrays are allocated on the heap by default. There is a way to allocate arrays on the stack with the stackalloc keyword, but you almost never want to do that.
In your case of overwriting a[x] with a pointer to a different object, the former object will probably get garbage collected. Garbage collection works by periodically traversing allocated data structures, starting at the stack, static variables, thread locals, etc, and following every pointer. Any object that is traversed is "live" and is not deleted. Any objects that it can't reach by following all of the possible pointers are considered "unreachable" and are automatically deleted. If you had any other reachable variables pointing to the former contents of a[x], that would keep the object alive. There are some surprising cases where you can keep a reference to an object stored for longer than you intended (usually as delegates) and can be considered a "memory leak".
There is another alternative to just 'use structs or classes to control copying behavior' - using immutable classes. The common problem with accidentally copying a class is that when you modify it via one variable, you affect anything else referring to it. If you make every field 'readonly', it results in something that behaves kind of like strings do: strings in C# are classes, but you can't modify their contents. So it's safe to copy them freely because you know you can't introduce mysterious bugs because you can't modify their contents. You are forced to create new strings instead. Copies are cheap to perform (because you're only copying a pointer address), but you get the safety you would from the uniqueness of a struct.
If you're using Unity and monodevelop can't debug, that will happen if you've switched to the .Net 4.6 runtime. Monodevelop is being phased out. The recommendations are (on OSX) to use Visual Studio for Mac or JetBrains Rider. On Windows, use Visual Studio 2017 (either Community or Professional, depending on whether you're doing commercial work or not). All of these IDEs are vastly superior to MonoDevelop, can debug Unity, and will provide a much better overall experience working with C#.