views:

78

answers:

3

I'm not even sure how I should phrase this question. I'm passing some CustomStruct objects as parameters to a class method, and storing them in a List. What I'm wondering is if it's possible and more efficient to add multiple references to a particular instance of a CustomStruct if a equivalent instance it found.

This is a dummy/example struct:

public struct CustomStruct
{
    readonly int _x;
    readonly int _y;
    readonly int _w;
    readonly int _h;
    readonly Enum _e;
}

Using the below method, you can pass one, two, or three CustomStruct objects as parameters. In the final method (that takes three parameters), it may be the case that the 3rd and possibly the 2nd will have the same value as the first.

List<CustomStruct> _list;

public void AddBackground(CustomStruct normal)
{
    AddBackground(normal, normal, normal);
}

public void AddBackground(CustomStruct normal, CustomStruct hover)
{
    AddBackground(normal, hover, hover);
}

public void AddBackground(CustomStruct normal, CustomStruct hover, CustomStruct active)
{
    _list = new List<CustomStruct>(3);
    _list.Add(normal);
    _list.Add(hover);
    _list.Add(active);
}

As the method stands now, I believe it will create new instances of CustomStruct objects, and then adds a reference of each to the List.

It is my understanding that if I instead check for equality between normal and hover and (if equal) insert normal again in place of hover, when the method completes, hover will lose all references and eventually be garbage collected, whereas normal will have two references in the List. The same could be done for active.

That would be better, right? The CustomStruct is a ValueType, and therefore one instance would remain on the Stack, and the three List references would just point to it. The overall List size is determined not by the object Type is contains, but by its Capacity. By eliminating the "duplicate" CustomStuct objects, you allow them to be cleaned up.

When the CustomStruct objects are passed to these methods, new instances are created each time. When the structs are added to the List, is another copy made? For example, if i pass just one CustomStruct, AddBackground(normal) creates a copy of the original variable, and then passes it three times to Addbackground(normal, hover, active). In this method, three copies are made of the original copy. When the three local variables are added to the List using Add(), are additional copies created inside Add(), and does that defeat the purpose of any equality checks as previously mentioned?

Is there a better way to handle this? Should the structs be passed as references to the methods to get around this?

Am I missing anything here?

+1  A: 

Note that everytime you pass a struct to a function as a parameter, or assign one variable to another, or in your case, add it to a list, a copy of the entire struct is created (in your case, 20 bytes will be copied).

Also note that there are no GC references to structs expect (I think) in some circumstances.


You will only get memory savings if you use a class instead of a struct - that way you can test for equality like you suggest.

logicnp
re "I think": even if the struct is indirectly on the heap, the GC reference is to the containing class, not directly to the struct
Marc Gravell
Thanks for the clarification.
logicnp
+2  A: 

You're talking about garbage collection and structs in the same line, which doesn't make sense. Ultimately, every time you look at a struct it is likely to copy itself. If you want to re-use the same reference, then you either need a class, or you need to wrap the struct in a class (boxing; either inbuilt or manual).

What actually happens in your code is:

_list.Add(normal); // a **copy** of normal is set into the list
_list.Add(hover); // a **copy** of hover is set into the list
_list.Add(active); // a **copy** of active is set into the list

Which when you think about it is (space-wise) identical to:

_list.Add(normal); // a **copy** of normal is set into the list
_list.Add(normal); // a **copy** of normal is set into the list
_list.Add(normal); // a **copy** of normal is set into the list

Note that the copy doesn't go onto the stack (the struct=stack is a common myth). The data goes into the array (inside the List<>) which is on the heap. Passing the structs arount by reference would make no difference whatsoever to the behaviour of Add (it simply avoids copying the struct when passing it into this method, but memory-copy is fast).

Marc Gravell
A: 

It sounds like you're mixing up the definition for value types and reference types a bit. Struct are value types, so variables store the actual values. An instance of a struct is not an object, and it will not be collected by the GC. A reference type variable stores a reference to an instance. Instances are stored on the heap and will eventually be collected by the GC.

Your list of structs is a reference type itself, so it will be stored on the heap along with its values. Each time you add an entry to the list the data is copied from the local representation in your method to one of the slots in the list. If you store the same value multiple times, it will be copied multiple times. It is just list a list of ints. If you have the value 42 ten times, you will find ten copies of the value 42 in memory for the list.

Brian Rasmussen