Dear All, I'm not a C# expert so the answer may be trivial. I have a List<Bounds>. If I call Encapsulate directly on an element of the List it does not work. While if I assign the item to a local variable then the Encapsulate works fine. Why? Example: Code (CSharp): List<Bounds> bounds = new List<Bounds>(); Bounds b1 = new Bounds(new Vector3(), new Vector3(2,0,0)); bounds.Add(b1); Bounds b2 = new Bounds(new Vector3(1,0,0), new Vector3(2,0,0)); bounds[0].Encapsulate(b2); // here bounds[0] has not changed Bounds debug = bounds[0]; debug.Encapsulate(b2); // here debug has been increased correctly
Did you actually check if bounds[0] changed, or did you check if b1 changed? Bounds are structs and structs are value types, so they get copied by value, not by reference, meaning b1 and bounds[0] contain the same data, but are not the same - thus changing bounds[0] does not change b1 and vice versa. So how did you check if the size changed? That's not in your example.
OK thanks, I didn't realize that Bounds is a struct not a class. I checked the value of bounds[0] with the debugger, after "bounds[0].Encapsulate(b2);" line. Anyway I guess the point is that Bounds is a struct.
I experimented a little w/ this issue: It turns out when indexing a List I couldn't change the value of the item, I had to use and temp variable and reassign it to the List. But using a System.Array worked just fine. Also I couldn't explicitly get a reference from List with ref. That's weird... Code (CSharp): public class Boundings : MonoBehaviour { public List<Bounds> bounds; // System.Collections.Generic.List<T> public Bounds b1; public Bounds b2; void Start() { bounds = new List<Bounds>(); b1 = new Bounds(new Vector3(), new Vector3(2, 1, 1)); b2 = new Bounds(new Vector3(1, 0, 0), new Vector3(2, 1, 1)); bounds.Add(b1); } void Update() { if (Keyboard.current.bKey.wasPressedThisFrame) { bounds[0].Encapsulate(b2); // This WON'T work b1.Encapsulate(b2); bounds[0] = b1; // This WORKS } } private void OnDrawGizmos() { Gizmos.color = Color.red; if(bounds.Count>0) Gizmos.DrawWireCube(bounds[0].center, bounds[0].size); } } Code (CSharp): public class Boundings : MonoBehaviour { public Bounds[] bounds; // System.Array public Bounds b1; public Bounds b2; void Start() { bounds = new Bounds[2]; b1 = new Bounds(new Vector3(), new Vector3(2, 1, 1)); b2 = new Bounds(new Vector3(1, 0, 0), new Vector3(2, 1, 1)); bounds[0] = b1; } void Update() { if (Keyboard.current.bKey.wasPressedThisFrame) { bounds[0].Encapsulate(b2); // This WORKS } } private void OnDrawGizmos() { Gizmos.color = Color.red; if(bounds.Length>0) Gizmos.DrawWireCube(bounds[0].center, bounds[0].size); } }
The behaviour you observe is due to how structs work. They are value types and thus copied when they're assigned. The example with the array works because you access the value where it's located in memory. That's different when you use the indexer of lists and other types with an indexer. In this case, they're basically more like syntax sugar for parameterized getter/setter functions. So you're actually getting a copy of the bounds.
I know about structs, but I didn't know about Lists. But how does that explain that Code (CSharp): bounds[0] = b1; works fine. You're setting the actual item not a copy of it. And isn't Array also a collection class? or is it just the actual CLR representation of T[] like Int32?
Even though one may say an array is like a collection, it's more like a raw type. It also doesn't meet the typical requirements, it cannot resize automatically, it does not have the common members that you'd find on a collection type, i.e. one that implements ICollection. When 'bounds' is an array, that'd just write the value (still a copy, because there's no other way) to that location you indexed. When 'bounds' is an indexer of any other type, e.g. a list, it'd act like a method. As mentioned earlier, the indexer [ ] can be understood as syntax sugar for getter/setter. If you had a setter, let's say void SetItem(int index, Bounds bounds), you would copy the argument just as well. Likewise, a getter Bounds GetItem(int index) would return a copy of the value at the supplied index.
I understand that but since collection bounds is a supposed to return a copy how come bounds[0] = b1; works fine? isn't bounds[0] just a copy of the first item? And likewise why when calling a function like bounds[0].Encapsulate(b1); that's supposed to modify its state, it doesn't work? That's my question? Why: Code (CSharp): bounds[0].Encapsulate(b2); // This WON'T work b1.Encapsulate(b2); bounds[0] = b1; // This WORKS
Like already mentioned, it functions as both, getter and setter. In this case, it doesn't query anything but sets the value. When you use encapsulate on the indexed item directly, you're effectively working on a temporary copy. If that is a pure value type, e.g. no hidden references and side effects, this wouldn't do anything useful but it is still valid syntax because it could do something useful in some cases. It compiles into a getter which returns a copy and then calls the method on that copy. Another example, and that's where the compiler can even tell you this is not allowed and nonsensical, is when you try to assign a field of a temporary value (i.e. no reference).. Just do the same, but do not call a method, instead try to assign a field of these bounds. That would be flagged as an error by the compiler, as it only works when the item returned is any sort of reference.