183

What are your rules of thumb for when to use structs vs. classes? I'm thinking of the C# definition of those terms but if your language has similar concepts I'd like to hear your opinion as well.

I tend to use classes for almost everything, and use structs only when something is very simplistic and should be a value type, such as a PhoneNumber or something like that. But this seems like a relatively minor use and I hope there are more interesting use cases.

8
  • 5
  • 7
    @Frustrated Can't mark a question as a duplicate of something on another site, although it is certainly related.
    – Adam Lear
    Jul 12, 2011 at 17:28
  • @Anna Lear: I know, I voted to migrate not to close-as-duplicate. Once it's migrated, it can be closed as a duplicate properly. Jul 12, 2011 at 17:32
  • 5
    @Frustrated I don't think it needs to be migrated.
    – Adam Lear
    Jul 12, 2011 at 17:35
  • 16
    This seems like a question much more suited to P.SE vs. SO. Which is why I asked it here. Jul 12, 2011 at 17:46

5 Answers 5

183

The general rule to follow is that structs should be small, simple (one-level) collections of related properties, that are immutable once created; for anything else, use a class.

C# is nice in that structs and classes have no explicit differences in declaration other than the defining keyword; so, if you feel you need to "upgrade" a struct to a class, or conversely "downgrade" a class to a struct, it's mostly a simple matter of changing the keyword (there are a few other gotchas; structs can't derive from any other class or struct type, and they can't explicitly define a default parameterless constructor).

I say "mostly", because the more important thing to know about structs is that, because they are value types, treating them like classes (reference types) can end up a pain and a half. Particularly, making a structure's properties mutable can cause unexpected behavior.

For example, say you have a class SimpleClass with two properties, A and B. You instantiate a copy of this class, initialize A and B, and then pass the instance to another method. That method further modifies A and B. Back in the calling function (the one that created the instance), your instance's A and B will have the values given to them by the called method.

Now, you make it a struct. The properties are still mutable. You perform the same operations with the same syntax as before, but now, A and B's new values aren't in the instance after calling the method. What happened? Well, your class is now a struct, meaning it's a value type. If you pass a value type to a method, the default (without an out or ref keyword) is to pass "by value"; a shallow copy of the instance is created for use by the method, and then destroyed when the method is done leaving the initial instance intact.

This becomes even more confusing if you were to have a reference type as a member of your struct (not disallowed, but extremely bad practice in virtually all cases); the class would not be cloned (only the struct's reference to it), so changes to the struct would not affect the original object, but changes to the struct's subclass WILL affect the instance from the calling code. This can very easily put mutable structs in very inconsistent states that can cause errors a long way away from where the real problem is.

For this reason, virtually every authority on C# says to always make your structures immutable; allow the consumer to specify the properties' values only on construction of an object, and never provide any means to change that instance's values. Readonly fields, or get-only properties, are the rule. If the consumer wants to change the value, they can create a new object based on the values of the old one, with the changes they want, or they can call a method which will do the same. This forces them to treat a single instance of your struct as one conceptual "value", indivisible and distinct from (but possibly equatable to) all others. If they perform an operation on a "value" stored by your type, they get a new "value" which is different from their initial value, but still comparable and/or semantically equatable.

For a good example, look at the DateTime type. You cannot assign any of the fields of a DateTime instance directly; you must either create a new one, or call a method on the existing one which will produce a new instance. This is because a date and time are a "value", like the number 5, and a change to the number 5 results in a new value that is not 5. Just because 5+1 = 6 doesn't mean 5 is now 6 because you added 1 to it. DateTimes work the same way; 12:00 does not "become" 12:01 if you add a minute, you instead get a new value 12:01 that is distinct from 12:00. If this is a logical state of affairs for your type (good conceptual examples that aren't built in to .NET are Money, Distance, Weight, and other quantities of a UOM where operations must take all parts of the value into account), then use a struct and design it accordingly. In most other cases where the sub-items of an object should be independently mutable, use a class.

9
  • 2
    Good answer! I'd point one reading it to 'Domain Driven Development' methodology and book, that seems somewhat related to your opinion about why one should use classes or structures based on which concept you actually trying to implement Jul 12, 2011 at 18:31
  • 1
    Just a little detail worth mentioning : you can't define your own default constructor on a struct. You have to make do with the one that initialize everything to its default value. Usually, that's pretty minor, especially on class that are good candidate to be a struct. But it will mean that changing a class to a struct may imply more than just changing a keyword. Dec 20, 2012 at 22:04
  • 1
    @LaurentBourgault-Roy - True. There are other gotchas as well; structs can't have an inheritance hierarchy, for instance. They can implement interfaces, but cannot derive from anything other than System.ValueType (implicitly by their being structs).
    – KeithS
    Dec 20, 2012 at 22:15
  • As a JavaScript dev I have to ask two things. How are object literals that different from typical usages of structs and why is it important that structs be immutable? Dec 21, 2012 at 5:50
  • 1
    @ErikReppen: In answer to both of your questions: When a struct is passed into a function, it is passed by value. With a class, you are passing around a reference to the class (still by value). Eric Lippert discusses why this is a problem: blogs.msdn.com/b/ericlippert/archive/2008/05/14/… . KeithS's final paragraph also covers these questions.
    – Brian
    Dec 21, 2012 at 14:35
18

The answer: "Use a struct for pure data constructs, and a class for objects with operations" is definitely wrong IMO. If a struct holds a large number of properties then a class is nearly always more appropriate. Microsoft often says, from an efficiency point of view, if your type is larger than 16 bytes it should be a class.

https://stackoverflow.com/questions/1082311/why-should-a-net-struct-be-less-than-16-bytes

4
  • 1
    Absolutely. I'd upvote if I could. I don't get where the idea came from that structs are for data and objects are not. A struct in C# is just a mechanism for allocating on the stack. Copies of the entire struct are passed around instead of just a copy of an object pointer.
    – mike30
    Dec 20, 2012 at 17:30
  • 2
    @mike - C# structs are not necessarily allocated on the stack.
    – Lee
    Dec 20, 2012 at 18:37
  • 2
    @mike The "structs are for data" idea probably comes from the habit acquired by many years of C programming. C has no classes and its structs are data-only containers.
    – Konamiman
    Dec 21, 2012 at 11:07
  • Obviously there are at least 3 C programmers (got upvoted) having read Michael K's answer ;-)
    – bytedev
    Jan 10, 2013 at 16:01
10

The most important difference between a class and a struct is what happens in the following situation:

  Thing thing1 = new Thing();
  thing1.somePropertyOrField = 5;
  Thing thing2 = thing1;
  thing2.somePropertyOrField = 9;

What should be the effect of the last statement on thing1.somePropertyOrField? If Thing a struct, and somePropertyOrField is an exposed public field, the objects thing1 and thing2 will be "detached" from each other, so the latter statement will not affect thing1. If Thing is a class, then thing1 and thing2 will be attached to each other, and so the latter statement will write thing1.somePropertyOrField. One should use a struct in cases where the former semantics would make more sense, and should use a class in cases where the latter semantics would make more sense.

Note that while some people advise that a desire to make something mutable is an argument in favor of it being the class, I would suggest the reverse is true: if something which exists for the purpose of holding some data is going to be mutable, and if it will not be obvious whether instances are attached to anything, the thing should be a struct (likely with exposed fields) so as to make clear that instances are not attached to anything else.

For example, consider the statements:

  Person somePerson = myPeople.GetPerson("123-45-6789");
  somePerson.Name = "Mary Johnson"; // Had been "Mary Smith"

Will the second statement alter the information stored in myPeople? If Person is an exposed-field struct, it won't, and the fact that it won't will be an obvious consequence of its being an exposed-field struct; if Person is a struct and one wishes to update myPeople, one would clearly have to do something like myPeople.UpdatePerson("123-45-6789", somePerson). If Person is a class, however, it may be much harder to determine whether the above code would never update the contents of MyPeople, always update it, or sometimes update it.

With regard to the notion that structs should be "immutable", I disagree in general. There are valid usage cases for "immutable" structs (where invariants are enforced in a constructor) but requiring that an entire struct must be rewritten any time any part of it changes is awkward, wasteful, and more apt to cause bugs than would simply exposing fields directly. For example, consider a PhoneNumber struct whose fields, include among others, AreaCode and Exchange, and suppose one has a List<PhoneNumber>. The effect of the following should be pretty clear:

  for (int i=0; i < myList.Count; i++)
  {
    PhoneNumber theNumber = myList[i];
    if (theNumber.AreaCode == "312")
    {
      string newExchange = "";
      if (new312to708Exchanges.TryGetValue(theNumber.Exchange), out newExchange)
      {
        theNumber.AreaCode = "708";
        theNumber.Exchange = newExchange;
        myList[i] = theNumber;
      }
    }
  }

Note that nothing in the above code knows or cares about any fields in PhoneNumber other than AreaCode and Exchange. If PhoneNumber were a so-called "immutable" struct, it would be necessary either that it provide a withXX method for each field, which would return a new struct instance which held the passed-in value in the indicated field, or else it would be necessary for code like the above to know about every field in the struct. Not exactly appealing.

BTW, there are at least two cases where structs may reasonably hold references to mutable types.

  1. The semantics of the struct indicate that it's storing the identity of the object in question, rather than as a shortcut for holding the object's properties. For example, a `KeyValuePair` would hold the identities of certain buttons, but would not be expected to hold a persistent information about those button's positions, highlight states, etc.
  2. The struct knows that it holds the only reference to that object, nobody else will get a reference, and any mutations which will ever be performed to that object will have been done before a reference to it gets stored anywhere.

In the former scenario, the object's IDENTITY will be immutable; in the second, the class of the nested object may not enforce immutability, but the struct holding the reference will.

10
  • Some years after I initially read an upvoted this answer, I came here again by change and I am wishing that I could upvote it twice. Working with ORM and entities represented as structs feels far more transparent, threadsafe and inthuitive. I'd still stick to the immutability though, nevertheless, the side effects would be way more contained May 28, 2020 at 18:26
  • @IvayloSlavov: What does the phony "immutability" buy? If a structure method chains to any outside methods, and those methods alter the value of the structure upon which that method is called, the value of this within that method will change despite the fact that it's supposedly "immutable".
    – supercat
    May 28, 2020 at 19:37
  • @IvayloSlavov: If one is using an "immutable" struct, how should one write code which needs to modify two fields and leave the rest alone? If one uses an exposed-field struct and adds new fields, code which updates other fields will leave the new ones unaltered whether or not it is aware of them. Further, if one performs myThing.x.y.z.someMethod() where every item in chain is an exposed-field structure, code will be more efficient than if any part of the chain is qualified read-only.
    – supercat
    May 28, 2020 at 19:46
  • 1
    @IvayloSlavov: I guess the best way of describing my philosophy would be to say that one should generally recognize and expose three kinds of object types in .NET: class objects, structs with behaviors beyond what outside code could do directly (non-trivial), and trivial structs. Structs with non-trivial behavior should be immutable when practical, and but structs without useful non-trivial behavior should be trivial. Since an immutable struct with no behaviors beyond what outside code could do would be useless, that implies that mutable trivial structs must be mutable.
    – supercat
    May 29, 2020 at 16:04
  • 1
    @IvayloSlavov: In other words, the real question shouldn't be whether a struct should be "mutable", but whether it should be non-trivial. If there is a reason for a struct to be non-trivial, that would generally imply that it should be immutable. If, however, there is no reason for a struct to be non-trivial, then it should be trivial which would imply that (if trivial) it must be mutable (or else it would have to be non-trivial).
    – supercat
    May 29, 2020 at 16:07
8

I tend to use structs only when there is one method is needed thus making a class seems too "heavy". Thus sometimes I treat structs as lightweight objects. BEWARE: this does not always work and is not always the best thing to do, so it really depends on the situation!

EXTRA RESOURCES

For more formal explanation, MSDN says: http://msdn.microsoft.com/en-us/library/ms229017.aspx This link verifies what I mentioned above, structs should only be used for housing a small amount of data.

3
  • 6
    Questions on another site (even if that site is Stack Overflow) don't count as duplicates. Please expand your answer to include an actual answer and not just links to other places. If the links ever change, your answer as it's written now will be useless and we like to preserve information and make Programmers as self-contained as possible. Thanks!
    – Adam Lear
    Jul 12, 2011 at 17:27
  • 2
    @Anna Lear Thanks for letting me know, will keep that in mind from now on!
    – rrazd
    Jul 12, 2011 at 17:37
  • 2
    -1 "I tend to use structs only when there is one method is needed thus making a class seems too "heavy"." ... heavy? what does that really mean? ... and are you really saying that just cos there is one method then it should probably be a struct?
    – bytedev
    Dec 19, 2016 at 14:03
2

Use a struct for pure data constructs, and a class for objects with operations.

If your data structure needs no access control and has no special operations other than get/set, use a struct. This makes it obvious that all that structure is is a container for data.

If your structure has operations that modify the structure in any more complex way, use a class. A class also can interact with other classes via arguments to methods.

Think of it as the difference between a brick and an airplane. A brick is a struct; it has lenght, width and height. It can't do much. An airplane could have multiple operations that mutate it somehow, like moving control surfaces and changing engine thrust. It would be better as a class.

2
  • 2
    "Use a struct for pure data constructs, and a class for objects with operations" in C# this is nonsense. See my answer below.
    – bytedev
    Mar 25, 2014 at 16:38
  • 1
    "Use a struct for pure data constructs, and a class for objects with operations." This is true for C/C++, not C#
    – azerty
    Aug 3, 2017 at 17:39

Not the answer you're looking for? Browse other questions tagged or ask your own question.