1

I know that boxing is a popular concept with plenty of information available on it, but I have a few questions which I can't really find answers to:

1) If boxing leads to a value type (struct) being converted to an object (Reference type), or reference type, then why use a value type which will be boxed and incur a performance penalty? I am aware of the benefits and suitability in certain cases of either a struct or class. It is said (1) that values (value types) tend to live on the stack in a temporary storage space, but how long for? If I don't need the type, how can I ensure it is taken care of and disposed at that moment? Or is this where the disposable pattern comes into play? I assume the reason to use a struct will be due to its benefits.

Interestingly, if I use a struct to store two strings and a DateTime field, the struct will hold two references (strings) and the DateTime together. I obviously assume this is quicker than the values being scattered. Is there anything I need to be aware of in this design? (2).

1) http://en.csharp-online.net/Classes, Structs, and Objects—Boxing and Unboxing

2) http://dotnetperls.com/Content/Struct-Examples.aspx

I have done a search on here for the answers I am after, but no luck. I usually do searches on this site for topics such as GC, generics, exception handling, etc, as there is a lot of wisdom to learn and share.

Thanks for the (potential) education to all posters! Please excuse any potential naivety. Learning the internals takes me nicely to spending some time on understanding IL, etc (something to tackle, soon).

jpalecek
  • 47,058
  • 7
  • 102
  • 144
GurdeepS
  • 65,107
  • 109
  • 251
  • 387

4 Answers4

4

If you never pass the value type into a reference variable then boxing will not occur. When you don't know then answer the following questions:

  • Act like primitive types.
  • Have an instance size under 16 bytes.
  • Are immutable.
  • Value semantics are desirable.

I also usually consider what is the lifetime of such a variable. If it is a local variable used within a method then I would tent to use struct (otherwise class).

David Pokluda
  • 10,693
  • 5
  • 28
  • 26
  • These are taking from the .NET guidelines and are generally good questions to ask yourself. – Ben S Mar 06 '09 at 22:38
  • I always hear about wanting value semantics - what would be a time such a thing is desired? – GurdeepS Mar 06 '09 at 22:38
  • http://msdn.microsoft.com/en-us/library/aa664472%28VS.71%29.aspx Talks about value semantics and when they are useful. – Ben S Mar 06 '09 at 22:40
  • Also, if I use a variable in the scope of a method and it is a class, should I wrap it in a struct? – GurdeepS Mar 06 '09 at 22:40
  • If the variable is a primitive, there's no point since those have value semantics already. If the variable is an object that has the four above qualities, it might be a good candidate to turn into a struct rather than an object. – Ben S Mar 06 '09 at 22:43
  • I actually meant for an existing .NET type, which is an object but may be a candidate for treatment as a struct. – GurdeepS Mar 06 '09 at 22:51
  • The .NET types are usually set to the correct reference vs value type for their most common uses. Wrapping one you just add another layer and make your code more confusing. – Ben S Mar 06 '09 at 22:53
1

You should use value types because of their logical benefit, not the performance gains. That being said, because value types are managed on the stack, do not need to participate in garbage collection. If you have a type that is constantly created and discarded (like an int, float, double, etc), then you can get a good boost by turning these into structs. The thing to be careful of is that you should only really consider this if you can also make the struct immutable.

Michael Meadows
  • 27,796
  • 4
  • 47
  • 63
  • What is required to make a struct immutable? Am I right in thinking just give it a low visibility and make it readonly? – GurdeepS Mar 06 '09 at 22:54
  • Also, how does disposal work for value types on the stack anyway? – GurdeepS Mar 06 '09 at 22:55
  • Unfortunately, there are no semantics in C# for creating immutable types. It's simply a matter of making sure that its state cannot be changed after creation. – Michael Meadows Mar 07 '09 at 02:01
  • Since value types are created on the stack, they are not "allocated" so do not need to be "collected." They simply take up space in the stack for the block it is in scope of. When the block is popped from the stack, the variable no longer takes up memory. – Michael Meadows Mar 07 '09 at 02:04
1

A couple other things to consider -

First, you want to make sure structs are immutable (in general). Because of this, it's a good rule of thumb to not have structs containing reference types. Strings can be an exception to this since they're immutable in C#, but in terms of a general-purpose rule of thumb for design, I'd be wary of this.

Second, there's another use case for structs that wasn't mentioned so far - large numbers of small objects. If you have a large list or array of small objects, structs provide dramatically better cache coherency, and are absolutely critical. This is why most 3D engines use structs for points/vectors - they tend to have large arrays of points for vertices, etc.

This is something worth paying attention to if performance is an important part of your application. For example, in one of my apps, changing a single type from a class to a struct shaved 40% off a long running (>5 minute runtime) process. Having the objects close together in memory if you are using them repeatedly in heavy math computations can provide huge gains.

Now - in your case, having 2 strings and a DateTime probably won't see any improvements from this. The type of routines that would work on strings are probably not doing heavy computation (hopefully), ie: transforming a half a million points in space, or doing a large matrix solution, etc.

Finally - you'll notice that .net3.5sp1 made structs much more useful. Prior to 3.5sp1 (on x86), there was no inlining of methods with struct calls. This limited the performance gains possible via structs. Updating your framework can make old struct code much, much faster (in certain cases).

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • If I look at what types are value and which are reference, then a struct would work a select few value types and all the work with reference types happening in classes. So reference types (bar string) are immutable? – GurdeepS Mar 07 '09 at 13:23
  • Basically, but the other way around - Reference types are normally mutable types - ie: properties change their values (set). Structs, on the other hand, would be immutable. Basically, once a struct is constructed, you never want it to change. If you want a new value, you make a new struct. – Reed Copsey Mar 09 '09 at 17:13
0

You don't always need boxing, and with generics there is little need for that.
The memory used by value types (struct is a value type) will be claimed as
soon as the method ends/returns and you don't
need to do anything for that to happen.

Value types declared as instance members will be in memory until the object
is deleted by the GC.

Reference types are kept on the managed heap.
Reference types instantiated within a method will be deleted by the
garbage collector when no object is holding a reference to it.

GC works by itself and for the most part you should leave it alone.
You can not predict when an object is going to be deleted by the GC.

The Dispose pattern is used on reference types but will not force GC to delete
an object. It's usually used to free unmanaged resources.

For values in the stack consider the following:
Let's say you have a simple program with three methods, like below:
When this program is run, the Main method is run, and so on. Please follow the
numbers below:


Main
{
   // (0) Stack is empty
   int firstInt = 0;
   // (1) Stack now contains:
   //                     firstInt
   DoSomething1();
   // (7) Stack still contains:
   //                     firstInt
}
// Program ends

DoSomething()
{
   int anInteger = 0; 
   // (2) Stack now contains:
   //                    anInteger
   //                    firstInt
   DoMore()
   // (5) Stack now contains:
   //                     anInteger
   //                     firstInt
}
// (6) anInteger goes out of scope

DoMore
{
  int anotherInteger = 1; 
   // (3) Stack now contains:
   //                     anotherInteger
   //                     anInteger
   //                     firstInt
}
// (4) anotherInteger goes out of scope

Klinger
  • 4,900
  • 1
  • 30
  • 35