Click here to Skip to main content
Click here to Skip to main content

Structs in C#

, 20 Dec 2004
Rate this:
Please Sign up or sign in to vote.
Explains differences between classes and structs, and how to use structs.

Introduction

Every C/C++ programmer worth his salt would have used a struct one time or the other in his career. In C++, a struct is not very different from a class, except for the default accessibility of members. The situation is dramatically different in C#. This article attempts to introduce you gently to the differences between classes and structs in C#, and along the way, explains how to use structs correctly.

Why we need structs

Java, a language similar to C# in many ways, does not have structs, so what's the reason to add it to C#? The basic reason is the ability to create types with value semantics, which, if properly used, leads to better performance in a managed environment.

To substantiate, unlike Java, .NET supports the notion of value types and reference types (in Java, you can define only reference types). Instances of reference types get allocated in the managed heap and are garbage collected when there are no outstanding references to them. Instances of value types, on the other hand, are allocated in the stack, and hence allocated memory is reclaimed as soon as their scope ends. And of course, value types get passed by value (duh!), and reference types by reference. All C# primitive data types, except for System.String, are value types.

In C#, structs are value types, classes are reference types. There are two ways you can create value types, in C#, using the enum keyword and the struct keyword. Using a value type instead of a reference type will result in fewer objects on the managed heap, which results in lesser load on the garbage collector (GC), less frequent GC cycles, and consequently better performance. However, value types have their downsides too. Passing around a big struct is definitely costlier than passing a reference, that's one obvious problem. The other problem is the overhead associated with boxing/unboxing. In case you're wondering what boxing/unboxing mean, follow these links for a good explanation on boxing and unboxing. Apart from performance, there are times when you simply need types to have value semantics, which would be very difficult (or ugly) to implement if reference types are all you have.

Classes and Structs

Here's a definition of a struct in C#:

   public struct Foo
   {
      // Fields
      private string fooString;
      private int fooNumber;

      // Property
      public string FooString
      {
         get
         {
            return fooString;
         }
         set
         {
            fooString = value;
         }
      }

      // Method
      public int GetFooNumber()
      {
         return fooNumber;
      }
   }

As you can see, a struct is very much like a class, but there are some important differences, let's look into them in more detail.

1. Structs and Inheritance

structs derive from System.ValueType whereas classes derive from System.Object or one of its descendants. Of course, System.ValueType again derives from System.Object, but that's beside the point. structs cannot derive from any other class/struct, nor can they be derived from. However, a struct can implement any number of interfaces. Be aware, though, that when you treat the struct as an interface, it gets implicitly boxed, as interfaces operate only on reference types. So, if you do something like the following:

   struct Foo : IFoo
   {
      int x;
   }

and then:

    IFoo iFoo = new Foo();

an instance of Foo is created and boxed. All interface method calls then execute only on the boxed instance.

2. Constructors

Although the CLR allows it, C# does not allow structs to have a default parameterless constructor. The reason is that, for a value type, compilers by default neither generate a default constructor, nor do they generate a call to the default constructor. So, even if you happened to define a default constructor, it will not be called and that will only confuse you. To avoid such problems, the C# compiler disallows definition of a default constructor by the user. And because it doesn't generate a default constructor, you can't initialize fields when defining them, like:

   struct MyWrongFoo
   {
      int x = 1;
   }

Remember, the compiler puts all this initialization code into the constructor (every constructor), and because there's no default constructor, you can't do the initialization.

Now, for the fun part.. You normally instantiate a struct like this:

    Foo foo = new Foo();

As you had read earlier, even though you use the new operator, the struct gets allocated on the stack. More interesting is the fact that you say new Foo() and yet there is no default constructor. The call new Foo() does not result in a call to the parameterless constructor, all it does is initialize the struct's fields to null/zero (using the .InitObj IL Opcode). As a proof, the following snippet will compile happily:

   struct Foo
   {
      int x;
      public Foo(int x)
      {
         this.x = x;
      }
   }
      
   class FooTester
   {
      [STAThread]
      static void Main(string[] args)
      {
         Foo f = new Foo();
      }
   }

Note that I've defined an overloaded constructor and yet I'm able to call new Foo(). This simply shouldn't be possible if the call to new resulted in a constructor call.

The only rule is that you need to initialize all fields of a struct before using it. You can do that by:

  • calling new Foo().
  • calling an overloaded constructor. C# forces you to initialize all fields within every overloaded constructor, so there is no getting around the "initialize-everything" rule.
  • explicitly setting every field's value. For e.g.:
        Foo foo;
        foo.x = 0;

3. Destructors

You cannot define destructors (which are nothing but Finalize methods) for structs. If you ever thought (like me) that using destructors and structs, you can get deterministic finalization, forget it! The compiler straightaway flags it as an error. Of course, structs can implement IDisposable (it being an interface), so you can always use the dispose pattern (albeit with the extra boxing overhead).

4. Comparison against null

I know this is minor, but you can't compare an instance of a value type against null. Things are changing with 2.0 though, with the introduction of "nullable types".. but that's material for another article!

5. The readonly keyword

For a reference type, readonly prevents you from reassigning a reference to refer to some other object. It does not prevent you from changing the state of the referred object. For value types, however, readonly is like the const keyword in C++, it prevents you from changing the state of the object. This implies that you can't reassign it again, as that would result in reinitialization of all fields. The following piece of code demonstrates that.

class MyReferenceType
    {
        int state;

        public int State
        {
            get
            {
                return state;
            }
            set
            {
                state = value;
            }
        }
    }

    struct MyValueType
    {
        int state;

        public int State
        {
            get
            {
                return state;
            }
            set
            {
                state = value;
            }
        }
    }

    class Program
    {
        readonly MyReferenceType myReferenceType = new MyReferenceType();
        readonly MyValueType myValueType = new MyValueType();
        
        public void SomeMethod()
        {
            myReferenceType = new MyReferenceType(); // Compiler Error
            myReferenceType.State = 1234; // Ok

            myValueType = new MyValueType(); // Compiler Error
            myValueType.State = 1234; // Compiler Error
        }
    }

While it is the logical thing to do for value types, it can bite you in unexpected ways. Variables declared in foreach statements and using statements are implicitly readonly, so if you are using structs there, you won't be able to change their states.

When to use structs

So you've seen how structs and classes differ. Here's when structs are better:

  • You want your type to look and feel like a primitive type.
  • You create a lot of instances, use them briefly, and then drop them. For e.g., within a loop.
  • The instances you create are not passed around a lot.
  • You don't want to derive from other types or let others derive from your type.
  • You want others to operate on a copy of your data (basically pass by value semantics).

Here's when not to use structs:

  • The size of the struct (the sum of the sizes of its members) gets large. The reason is that beyond a particular size, the overhead involved in passing it around gets prohibitive. Microsoft recommends that the size of a struct should ideally be below 16 bytes, but it really is up to you. In case your struct has reference types as members, make sure you don't include the size of instances of reference types, just the size of the references.
  • You create instances, put them in a collection, iterate and modify elements in the collection. This will result in a lot of boxing/unboxing as FCL Collections operate on System.Object. Every addition will involve a boxing operation, and every modification will involve an unboxing followed by a boxing operation.

Conclusion

Some of the inefficiencies of using value types will go away with generics in C# 2.0, particularly when using collections, so things can only get better. It's great that C# allows you to choose how you want to implement your type, as a value or a reference type. Judicious use of value types can greatly increase application performance. Hopefully, this article will help you do that.

History

  • Updated on 18-December-2004 - Added content for the readonly keyword.
  • Updated on 20-October-2004 - Incorporated comments.
  • Posted on 20-October-2004.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

S. Senthil Kumar
Software Developer Atmel R&D India Pvt. Ltd.
India India
I'm a 27 yrs old developer working with Atmel R&D India Pvt. Ltd., Chennai. I'm currently working in C# and C++, but I've done some Java programming as well. I was a Microsoft MVP in Visual C# from 2007 to 2009.
 
You can read My Blog here. I've also done some open source software - please visit my website to know more.

Comments and Discussions

 
QuestionI found something Good Pinmemberwaxsantu5-May-14 6:36 
GeneralMy vote of 5 Pinmemberpashad23-Jan-12 18:46 
GeneralMy vote of 5 Pinmemberpinejac5-Dec-11 8:53 
QuestionGood article : You can add usage of Union in C# in this article PinmemberAsh206-Sep-11 20:26 
Questionarray of structs??? PinmemberEdward1114-Nov-09 19:10 
GeneralNice! (Vote 5) PinmemberTheArchitectualizer12-Jul-09 2:31 
GeneralRe: Nice! (Vote 5) PinmemberS. Senthil Kumar12-Jul-09 5:47 
Generalber ber ber Pinmembermyallies9-Nov-07 0:10 
GeneralC++ struct to C# struct Pinmemberproteus18916-Oct-07 10:14 
Questiontrouble witch ucing struct from C dll Pinmemberyucikala21-Mar-07 23:56 
Questionhow to declare array of structures containg array of structure objects in C# Pinmemberkalaveer29-Oct-06 19:48 
AnswerRe: how to declare array of structures containg array of structure objects in C# PinmemberS. Senthil Kumar29-Oct-06 20:04 
GeneralRe: how to declare array of structures containg array of structure objects in C# Pinmemberkalaveer29-Oct-06 21:00 
GeneralRe: how to declare array of structures containg array of structure objects in C# PinmemberS. Senthil Kumar29-Oct-06 21:49 
QuestionRe: how to declare array of structures containg array of structure objects in C# Pinmemberkalaveer29-Oct-06 23:00 
AnswerRe: how to declare array of structures containg array of structure objects in C# PinmemberS. Senthil Kumar30-Oct-06 2:58 
QuestionHow about Struct in DataObjectSource ? Pinmembertanstudio22-Oct-06 16:28 
AnswerRe: How about Struct in DataObjectSource ? PinmemberS. Senthil Kumar22-Oct-06 19:21 
GeneralDangerous methods of structs PinmemberAlexey A. Popov19-Jun-06 9:10 
GeneralDuh! PinsussAnonymous31-Aug-05 17:30 
GeneralRe: Duh! PinmemberS. Senthil Kumar31-Aug-05 19:45 
GeneralAnonymous is right (Re: Duh!) PinmvpSAKryukov23-Jan-12 18:49 
GeneralRe: Anonymous is right (Re: Duh!) PinmemberS. Senthil Kumar23-Jan-12 20:35 
GeneralRe: Anonymous is right (Re: Duh!) PinmvpSAKryukov1-Feb-12 22:47 
GeneralRe: Anonymous is right (Re: Duh!) PinmemberS. Senthil Kumar1-Feb-12 23:55 
GeneralRe: Anonymous is right (Re: Duh!) PinmvpSAKryukov2-Feb-12 7:17 
GeneralRe: Anonymous is right (Re: Duh!) PinmvpSAKryukov2-Feb-12 7:32 
GeneralRe: Anonymous is right (Re: Duh!) PinmemberS. Senthil Kumar2-Feb-12 22:28 
GeneralRe: Duh! PinmvpSAKryukov23-Jan-12 18:40 
GeneralRe: Duh! PinmvpSAKryukov23-Jan-12 18:52 
QuestionPerformance numbers? PinmemberJim Wiese (aka Spunk)14-Jan-05 4:31 
QuestionWhat happens here? PinmemberPavlos Touboulidis7-Jan-05 0:50 
AnswerRe: What happens here? PinmemberS. Senthil Kumar7-Jan-05 7:58 
Generalclear some fog... PinsussAnonymous22-Dec-04 23:07 
GeneralMathematical structs are better examples PinmemberFrank Hileman21-Dec-04 10:31 
QuestionAlternative to comparing structs to null? PinsussAnonymous30-Nov-04 17:31 
AnswerRe: Alternative to comparing structs to null? PinmemberJon Rista21-Dec-04 6:59 
GeneralRe: Alternative to comparing structs to null? PinmemberJudah Himango21-Dec-04 12:09 
GeneralRe: Alternative to comparing structs to null? PinsussAnonymous16-Jan-05 17:10 
GeneralRe: Alternative to comparing structs to null? PinmemberPelotas17-Jun-08 10:44 
QuestionWhat aout Union Pinmemberkrishnadevank4-Nov-04 23:07 
AnswerRe: What aout Union PinsussB Vidyadhar Joshi22-Nov-04 19:38 
GeneralRe: What aout Union PinmemberS. Senthil Kumar23-Nov-04 2:34 
GeneralRe: What aout Union PinmemberJon Rista21-Dec-04 7:03 
GeneralRe: What aout Union PinmemberLeviJackson31-Jan-05 5:36 
GeneralPerformance is not the only reason for valuetypes PinmemberRüdiger Klaehn28-Oct-04 2:50 
GeneralRe: Performance is not the only reason for valuetypes PinstaffNishant S28-Oct-04 3:06 
GeneralRe: Performance is not the only reason for valuetypes PinmemberRüdiger Klaehn30-Oct-04 6:11 
GeneralRe: Performance is not the only reason for valuetypes PinstaffNishant S30-Oct-04 14:16 
GeneralRe: Performance is not the only reason for valuetypes PinmemberS. Senthil Kumar30-Oct-04 20:17 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.141015.1 | Last Updated 21 Dec 2004
Article Copyright 2004 by S. Senthil Kumar
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid