
Best Practices for Designing Structure Types
• Structures are value types allocated on the stack.
• In contrast, classes are heap allocated and managed by the CLR garbage collector.
• For this reason, structures are more lightweight than classes.
• Because structures are value types, your custom structures should always:
• Override System.Object.Equals() and System.Object.GetHashCode() to perform value based
comparison semantics.
• Also override System.Object.ToString() to return a sting representing the value type’s state.
// C#
struct MyPoint
{
public int X, Y;
public MyPoint(int xPos, int yPos)
{ X = xPos; Y = yPos; }
public override string ToString()
{ return string.Format("[{0}, {1}", X, Y); }
public override int GetHashCode()
{ return ToString().GetHashCode(); }
public override bool Equals(object obj)
{
if (obj is MyPoint)
{
MyPoint temp = (MyPoint)obj;
if(temp.ToString() == this.ToString())
return true;
else
return false;
}
return false;
}
}
' VB 2005
Structure MyPoint
Public X As Integer, Y As Integer
Sub New(ByVal xPos As Integer, ByVal yPos As Integer)
X = xPos : Y = yPos
End Sub
Public Overrides Function ToString() As String
Return String.Format("[{0}, {1}", X, Y)
End Function
Public Overrides Function GetHashCode() As Integer
Return ToString().GetHashCode()
End Function
Public Overrides Function Equals(ByVal obj As Object) As Boolean
If TypeOf obj Is MyPoint Then
Dim temp As MyPoint = DirectCast(obj, MyPoint)
If temp.ToString() = Me.ToString() Then
Return True
Else
Return False
End If
End If
Return False
End Function
End Structure
• As well, consider overloading the equality operators so your types work symmetrically with the CLR
value types (e.g., System.Int32, System.Int16, etc).
• This makes it simple to use your types within conditional logic.
• Be aware of the impact of implementing interfaces on structures!
• When calling code accesses interface members implemented on a structure, you will incur boxing and
unboxing penalties!
• Recall this feature allows us to pretend everything is an object, however the downside is the stack
heap memory transfer.
• For example, assume we have implemented ICloneable on the MyPoint type.
// C#
struct MyPoint : ICloneable
{
public object Clone()
{
return new MyPoint(this.X, this.Y);
}
...
}
' VB 2005
Structure MyPoint
Implements ICloneable
Public Function Clone() As Object Implements System.ICloneable.Clone
Return New MyPoint(Me.X, Me.Y)
End Function
End Structure
• When we now access the interface members via the interface, a secret box / unbox occurs:
• Even worse, if the interface members operate on state data of the structure, you are modifying the
boxed copy, not the local stack allocated copy!
• Thus, you may find fields set to their default value when you thought you changed the value!
• Consider the following code.
// C#
static void Main(string[] args)
{
MyPoint p = new MyPoint(10, 10);
ICloneable i = (ICloneable)p; // Box!
MyPoint ptCopy = (MyPoint)i.Clone(); // Unbox!
}
' VB 2005
Sub Main()
Dim p As New MyPoint(10, 10)
Dim i As ICloneable = DirectCast(p, ICloneable) ' Box!
Dim ptCopy = DirectCast(i.Clone(), MyPoint) ' Unbox!
End Sub
• Consider the resulting CIL code seen through the eyes of ildasm.exe.
• Note the box and unbox opcodes.
• However, if you call the interface members directly from the object level, you avoid the boxing /
unboxing penalties.
• This cannot be achieved when making use of explicit interface implementation!
• Given this, avoid implementing interface members on structures using explicit interface implementation.
• When implementing structures which must perform internal clean up of unmanaged resources,
implement IDisposable.
• Recall that structures are not allocated on the heap, and therefore cannot support a finalizer method.
Designing Structure Types
Table of Contents
Copyright (c) 2008. Intertech, Inc. All Rights Reserved. This information is to be used exclusively as an
online learning aid. Any attempts to copy, reproduce, or use for training is strictly prohibited.
Courseware
Training Resources
Tutorials