Structs tend to be underutilized in .NET, and I think part of that is that developers aren’t quite sure where they fit. In an effort to make structs easier to use, various iterations of .NET have added a lot of class-like features to structs. Unfortunately, this tends to obscure the issue even further. There’s currently almost nothing you can do in a class that you can’t do in a struct and vice-versa, so why would anyone use anything but a regular class?
Discussions about the differences tend to revolve around memory management, and they’re usually incorrect. They talk about classes being stored on the heap and structs being stored on the stack. Not only is this not accurate, but it’s also not necessarily helpful information from a design perspective.
Then, people start talking about value versus reference types, which is a much more accurate categorization, but once again is of limited value from a design perspective.
So, here’s my nickel test for whether something should be a class or a struct:
If you take away the unique identifier field, could two instances with completely matching properties still theoretically be two, different things?
This can be kind of a difficult question for some people because many people (sadly) learn application development from a database-first perspective, and if you see two rows that have exactly the same data except for the unique identifier, we’re conditioned to think of that as a duplicate and not two actually different entities. And, honestly, that’s probably what it is.
That’s why my test has the word “theoretically” in it.
If you have two Persons with the same name and same address, they are probably a duplicate, but theoretically, they could be two, different people who have the same name and live at the same address. All the values being the same does not guarantee they are actually the same person. Therefore, a Person is a class.
However, the name “John Wesley Smith” is always the name “John Wesley Smith” no matter how many people share it. If all the values are the same, they are the same thing. They could not, theoretically, be two different names with all the same values. That doesn’t even make any sense. Two Names that have exactly the same FirstName, MiddleName, and LastName are actually the exact same name. So Name is a struct, not a class.
Address is (generally) the same way. If you have two Addresses, and they have the exact same StreetAddress1, StreetAddress2, City, State, and Zip, then they are the exact same address. They could not theoretically be two, different Addresses with the exact same specifications. So, Address is a struct.
Two Movies could have the same Title, same Director, and same Cast and still, theoretically, be two different Movies (however unlikely that might be), so Movie is a class.
What we’re really getting at here is how you define identity. Two different People are always two different people even if everything about them is exactly the same. Two different Names are actually the same name if everything about them is exactly the same.
This test is just an offhand test; it’s not a rule. An Address can be a class depending on how you define and use it. A Name might theoretically be a struct but, once you measure how the memory for it is actually being allocated and its influence on performance, you might switch it to a class to see if it changes anything. Various mapping and serialization tools might be easier to use with one scheme or another. So, the little test isn’t the last word in whether or not something is a class or a struct, but from a design perspective, it might help point you in the right direction.