When do you use a struct instead of a class?

  • 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.

    @Frustrated Can't mark a question as a duplicate of something on another site, although it is certainly related.

    @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*.

    @Frustrated I don't think it needs to be migrated.

    This seems like a question much more suited to P.SE vs. SO. Which is why I asked it here.

    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

    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.

    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.

  • KeithS

    KeithS Correct answer

    9 years ago

    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.

    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

    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.

    @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).

    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?

    @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.

    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.

    SUCH a good answer. I understand the concept of immutability now. Thank you!

    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: 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.

License under CC-BY-SA with attribution


Content dated before 6/26/2020 9:53 AM