What about value types?
[Update: thanks to Wesner, I’ve fixed the list you should consider when using value types]
In the previous post, I’ve talked about some basic features related with reference types. If all the types were reference types, then our applications would really hurt in the performance department. In fact, hurt is really a kind way of putting it…Imagine having to go through all the hoops associated with creating a new reference type for allocating space for a simple integer…not funny, right?
And that’s why the CLR introduced value types. They’re ideal for simple and frequently used types. By default, value types are allocated in the stack (though they can end in the heap when they’re used as a field of a reference type). Whenever you declare a variable of a value type, that variable will hold the required space for saving a value of that type (instead of holding a reference for a memory position on the heap, like it happens with reference types). In practice, this means that value types aren’t garbage collected like reference types.
In C#, you create a new value type by creating a new structure (struct keyword) or a new enumeration (enum). Here are two examples:
public struct Student { public String Name { get; set; } public Int32 Age { get; set; } } public enum Sex { Male, Female }
Whenever you do that,you end up creating a new type which is derived from the abstract ValueType type (notice that ValueType inherits from Object). Trying to specify a base type for a struct results in a compilation error. There’s really nothing you can do about that,so get used to it. Notice, though, that you’re free to implement one or more interfaces if you wish to. Another interesting thing to notice is that all value types are sealed, making it impossible to reuse them as base for any other type.
Whenever you create a new enum, you’ll end up with a new type which inherits from System.Enum (which is itself derived from ValueType). There’s more to say about enums, but we’ll leave that for another post.
Creating an instance of a struct is as easy as declaring a variable of that type:
Student std = new Student(); std.Name = "Luis";
In the previous snippet, we’re forced to used new to make the C# compiler happy. With value types, the new operator doesn’t end up allocating space in heap because the C# compiler knows that Student is a value type and that it should be allocated directly on the stack (btw, it zeroes all the fields of the instance). Notice that you must use it (or initialize it in some other way) before accessing its fields. If you don’t, you’ll end up with the “use of unassigned field” compilation error.
Now that you’ve met value and reference types, you might be interested in knowing when to use one or the other. In my limited experience, I can tell you that I end up using reference types in most situations, though there are some scenarios where value types should be used:
- simple, immutable types which behave like primitive types are good candidates for value types. DateTime is probably the best known example of a type.
- “small” types (ie, types which required less than 16 bytes of allocated space) might be good candidates. Don’t forget method parameters! If the type isn’t passed into methods a lot, then you can probably relax the size rule.
Before you start using value types all over the place, you should also consider that:
- value types can be boxed (more about this in a future post).
- you
cannot*should* customize the way these types handle equality and identity. - you can’t use any other base than ValueType or Enum nor can you reuse the type as a base for another type.
- assigning an instance of a value type to another, you’re really doing a field by field copy. this means that assignments will always duplicate the amount of space necessary.
And that’s sums it up pretty nicely. Stay tuned for more.
2 comments so far
4:23 am - 9-17-2010
> you cannot customize the way these types handle equality and identity.
Not sure what you mean by not being able to customize equality and identity handling. You can redefine operator==, Equals, and GetHashCode() for value types.
8:35 am - 9-17-2010
yes, you”re right. it should have been *should* instead of *cannot*. thanks 🙂