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.

vela
  • 5
  • 3
RationalGeek
  • 10,077
  • 7
  • 38
  • 56
  • 5
    Possible duplicate: http://stackoverflow.com/questions/6267957/struct-vs-class – FrustratedWithFormsDesigner Jul 12 '11 at 16:26
  • 7
    @Frustrated Can't mark a question as a duplicate of something on another site, although it is certainly related. – Adam Lear Jul 12 '11 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*. – FrustratedWithFormsDesigner Jul 12 '11 at 17:32
  • 5
    @Frustrated I don't think it needs to be migrated. – Adam Lear Jul 12 '11 at 17:35
  • 15
    This seems like a question much more suited to P.SE vs. SO. Which is why I asked it here. – RationalGeek Jul 12 '11 at 17:46
  • Blog post by Rico Mariani of when one might use a struct in violation of various guidelines discussed here and elsewhere: http://blogs.msdn.com/b/ricom/archive/2006/09/07/745085.aspx – Brian Dec 21 '12 at 14:31
  • Besides being overly-broad, one of the most important rules (both here and on SO) is: `Have you thoroughly searched for an answer before asking your question?` This question shows no real evidence that you did any research, which would have been a simple task in this case considering there is a **wealth** of information online that covers this very topic. – arkon May 21 '16 at 01:14
  • If this is too broad - where on SE are we supposed to ask questions like this? The up votes and stars here clearly demonstrate people don't know where to ask this - and want it answered. – niico Aug 19 '18 at 08:50

5 Answers5

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.

KeithS
  • 21,994
  • 6
  • 52
  • 79
  • 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 – Maxim Popravko Jul 12 '11 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. – Laurent Bourgault-Roy Dec 20 '12 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 '12 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? – Erik Reppen Dec 21 '12 at 05: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: http://blogs.msdn.com/b/ericlippert/archive/2008/05/14/mutating-readonly-structs.aspx . KeithS's final paragraph also covers these questions. – Brian Dec 21 '12 at 14:35
  • Ah, thanks. I didn't know about the structs-by-value thing. I wish we had a JS version of that. I would say don't fear the mutability, but maybe that's just a paradigm that works out in JS for reasons I don't completely grasp yet. Or it's just bordering-on-instinctual habits you develop when everything is mutable, args can be overloaded in one function and there's a ton of ways that values can dynamically cast themselves. But the popularity of getters/setters in Java and C# is something I never understood and not really something I'm looking forward to in JS. – Erik Reppen Dec 23 '12 at 00:01
  • SUCH a good answer. I understand the concept of immutability now. Thank you! – Kristopher Dec 04 '15 at 15:49
  • But all of this could be achieved by just creating an immutable class, so why use struct? From what I understand the motivation is for **optimization purposes** - you want it on the stack not on the heap so garbage collection needed etc. or you want value semantics so the variable itself contains the content and then you could use Equals between two instances and it'll compare the actual values, it'll be passed by value when you pass it to a method, it'll never be null, you can copy it to another struct instance and changes to the copy won't modify the original (source) struct. Your opinion? – BornToCode Aug 11 '16 at 08:54
  • @BornToCode: I agree with your statement; I skipped over *why* you want a struct when you can live with the limitations, and the primary advantages are as you say. However, not all of these are advantages all the time (which is why, for instance, `Nullable` and the `?` type modifier exist), and you also have to be careful about assuming value types will always be on the stack; that's basically only true for locally-scoped value types (locally-declared struct variables and value-typed method parameters). My answer was focused on the "rule-of-thumb" aspect of the question. – KeithS Aug 12 '16 at 14:18
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

bytedev
  • 431
  • 3
  • 8
  • 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 '12 at 17:30
  • 2
    @mike - C# structs are not necessarily allocated on the stack. – Lee Dec 20 '12 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 '12 at 11:07
  • Obviously there are at least 3 C programmers (got upvoted) having read Michael K's answer ;-) – bytedev Jan 10 '13 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.

supercat
  • 8,335
  • 22
  • 28
  • 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 – Ivaylo Slavov May 28 '20 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 '20 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 '20 at 19:46
  • Imutability here would help keep track side effects of the changes, by actually eliminating them. I see an immutable class behve like String/DateTime where methods return a new instance having the expressed changes. Passing a mutable struct to methods causing side effects would cause inconsistent results that are hard to gasp at first - an example is a mutable field struct with both *mutable* reference types and value types as fields. Changing the reference type fields would be preserved in your struct after the method you passed it to completes, but not the value type fields. – Ivaylo Slavov May 28 '20 at 22:14
  • By making types of the reference type fields of a struct immutable, you'd fix the inconsistency. By keeping the entire struct itself immutable, you'll enforce a programming model in your code that deals with immutability. Immutability could help you write code that works better with async modifications without issuing locks, and is a little more transparent to the coder to what an object's state at a given moment is. Nevertheless, the suggested by you mutable-field structs are very much usable as long as they contain either immutable reference type fields, or value type ones. – Ivaylo Slavov May 28 '20 at 22:19
  • I would agree on the efficiency point, as the 2 cases you mentioned in your post are valid, it will be a good approach. My concerns are for situations where your code can be called by others, or used in a public API. Most developers would experience the inconsistency I described above as a bug, if they are allowed to reproduce it by the API design. For internal operations which cannot be altered by other code this is a good optimisation. – Ivaylo Slavov May 28 '20 at 22:27
  • @IvayloSlavov: Programmers who don't understand the difference between structures and value types may be confused by exposed-field value types. The solution to that, however, is not to regard such things as broken, but instead recognize that programmers who don't understand the difference between structures and value types should *learn*. It isn't hard, and knowing that something is a trivial exposed-field structure with members of particular names and types *fully specifies everything one would need to know about it*. – supercat May 28 '20 at 22:40
  • In reality, in an organization with multiple teams, or in an open source project, the majority of people have always preferred predictable and more straightforward behavior. Once someone wastes a day debugging the inconsistency from my older comments, voices of 'reason' will raise as benefits of this approach will be doubted. I start to feel we are arguing about this, while in fact I agree mutable field structs have their place in the good development practices. But since you brought up the topic of immutability's role, I felt obliged to point out the problems its absence could bring. – Ivaylo Slavov May 29 '20 at 11:44
  • 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 '20 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 '20 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.

rrazd
  • 1,398
  • 2
  • 12
  • 23
  • 6
    Questions on another site (even if that site is [SO]) 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 '11 at 17:27
  • 2
    @Anna Lear Thanks for letting me know, will keep that in mind from now on! – rrazd Jul 12 '11 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 '16 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.

Michael K
  • 15,539
  • 9
  • 61
  • 93
  • 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 '14 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 03 '17 at 17:39