31

We have seen lots of discussion in SO regarding the class vs struct in c#. Mostly ended with conclusions saying its a heap/stack memory allocation. And recommending to use structs in small data structures.

Now I have a situation to decide the simple data store among these two choices. Currenlty in our application we have thousands of classes, just acts as simple data stores (only exposed public fields) and they passed among different modules and services.

As per my understanding, I felt it's better to move ahead with struct instead classes for the performance reasons. Because these are simple data structures only act as data stores.

Before proceeding with this, I need some expert advice from the people who have experienced this struggle.

  • is my understanding correct?
  • I have seen most ORMs have classes as data stores. So I doubt there should a reason to go ahead with classes instead structs. what would that be?
Matthew Murdoch
  • 28,946
  • 26
  • 89
  • 125
RameshVel
  • 60,384
  • 28
  • 166
  • 207
  • ORM objects are not "plain-old" struct-like data because they wrap around another data store, abstracting the database component away so it looks like you're just accessing an object. All of that is done with methods and reflection, which are features of a class. – Andres Jaan Tack Dec 23 '09 at 07:29

9 Answers9

59

I would make the choice based on the following criteria

  • reference type vs value type semantics. If 2 objects are only equal if they are the same object, it indicates reference type semantics => class. If the value of its members defines equality (e.g. 2 DateTimes are equal if both represent the same point in time even if they are 2 distinct objects), value type semantics => struct
  • Memory footprint of the object. If the object is huge and frequently allocated, making it a struct would consume the stack much faster, hence I'd rather have it as a class. On the contrary, I'd rather avoid the GC penalty for small value types; hence make them a struct.
  • can you make the object immutable? I find structs great for 'value objects' - from the DDD book.
  • Would you face some boxing-unboxing penalty based on the usage of this object? If yes, go for class.
Gishu
  • 126,803
  • 45
  • 214
  • 298
15

A pretty cool, not so well known advantage of Structs over Classes is that there is an automatic implementation of GetHashcode and Equals in structs.
That's pretty useful when keys are required for dictionaries

The struct implementation of GetHashcode and Equals is based on the binary content of the struct instances + reflection for the reference members (like String members and other instances of classes)

So the following code works for GethashCode/Equals :

public struct Person
{
    public DateTime Birthday { get; set; }
    public int Age{ get; set; }
    public String Firstname { get; set; }
}
class Program
{
    static void Main(string[] args)
    {
        Person p1 = new Person { Age = 44, Birthday = new DateTime(1971, 5, 24), Firstname = "Emmanuel" };
        Person p2 = new Person { Age = 44, Birthday = new DateTime(1971, 5, 24), Firstname = "Emmanuel" };
        Debug.Assert(p1.Equals(p2));
        Debug.Assert(p1.GetHashCode() == p2.GetHashCode());
    }
}

Both assertions succeed when Person is a struct Both assertions fail if Person is a class instead of a struct

Reference : https://msdn.microsoft.com/en-Us/library/2dts52z7%28v=vs.110%29.aspx

Regards, best coding

Emmanuel DURIN
  • 4,450
  • 2
  • 24
  • 45
7

structs should be defined immutable where in classes should not. If you think your objects are going to be small and immutable you can go ahead with making them structs or else let them be classes.

this. __curious_geek
  • 40,897
  • 20
  • 108
  • 134
  • Whether or not one favors having anything be mutable, the notion that if something is mutable it should be a class is totally backward. Suppose `Rectangle` were a mutable class rather than a struct. It would be entirely unclear what should happen if one attempted `MyControl.Bounds.Width += 20;`. The fact that `Bounds` is a struct rather than a class makes it clear that the above won't work (it won't even compile), and that one must either copy `Bounds` to a temporary `Rectangle`, modify that, and then set bounds using the new rectangle, or else find some other means of... – supercat Feb 26 '12 at 17:54
  • ...updating the control's bounds (in practice, there's a method whose purpose is to update arbitrary combinations of fields in `Bounds`, but one would have to search through the definition of `Control`; by contrast, copying `Bounds` to a temporary `Rectangle`, modifying it, and setting it back works precisely as one should expect given only the information that `Rectangle` is a struct with mutable fields, and `Bounds` is a read-write property of type `Rectangle`. No further documentation required. – supercat Feb 26 '12 at 17:56
6

I can never really seem to remember, exactly how structs are different, but they are. In subtle ways. In fact, sometimes they come and bite you.

So. Unless you know what you are doing, just stick to classes.

I know this sounds a little newbie. I know I should right now go and look up the differences and display them here - but that has already been done by others. All I'm saying is that adding a different type of objects creates a semantical burden, a bit of extra complexity that you are wise to consider carefully.

If I remember correctly, one of the biggest problem is the value semantics of structs: Passing them around will result in different objects (as they get passed by value). If you then change some field in one place, beware that in all other places the field did not get changed! That is why everyone is recommending immutability for structs!

EDIT: For the case you are describing, structs won't work!

Daren Thomas
  • 61,662
  • 38
  • 143
  • 192
  • 1
    +1; structs offer benefits but carry constraints. Not understanding the constraints can negate the benefits. Unfortunately getting cut & dry answers as to the exact constraints to consider is not an easy task--so structs are often used without understanding them. Typically classes will provide nearly as good performance and more functions. Classes are the safe bet. – STW Dec 23 '09 at 07:37
  • @Darren, "Passing them around will result in different objects"- is a great point... i really understand the problem now.. :) – RameshVel Dec 23 '09 at 07:53
  • @Darren... got a silly doubt.. we can pass the structs as ref params.. will that solve the problem of immutability?? – RameshVel Dec 23 '09 at 07:57
  • it might. I wouldn't bother thinking this through, though, as this is just begging for stupid mistakes with hard to find bugs. How are you going to make sure nobody ever, *ever* forgets the ref keyword? – Daren Thomas Dec 23 '09 at 08:39
  • A struct is basically a group of storage locations stuck together. If `P` and `Q` are of type `Point` (which is a struct with fields `X` and `Y`), saying `P=Q;` is equivalent to `P.X=Q.X; P.Y=Q.Y;`. Note that copying a struct will copy all fields, public and private. If you think of things in those terms, it will be obvious why structs behave differently from classes. Regarding structs and classes as being "almost the same" is unwise. The fact that .net implicitly defines for each struct a class which has the same name, fields, properties, and methods may make structs seem like classes... – supercat Feb 26 '12 at 05:57
  • ...and probably contributes to some confusion. If one casts `Point` to `Object`, it will be converted into a class object of type `Point`. If you cast the object back to `Point` and store it in a variable of that type, fields `X` and `Y` from that object will be copied to the `Point` variable. – supercat Feb 26 '12 at 06:00
4

A class object has the advantage that it's possible to pass around a reference to it, with the scope and lifetime of such a reference being unlimited if it reaches outside code. A struct has the advantage that while it's possible to pass around short-lived references to them, it's not possible to pass around perpetual promiscuous references. This helps avoid having to worry about whether such references exist.

Some people have suggested that data holders which are mutable should not be structs. I emphatically disagree. Entities which exists for the purpose of holding data should, in many cases, be structs, especially if they are mutable. Eric Lippert has posted many times that he considers mutable value types evil (search under tags "mutable" and "struct"). It is certainly true that .net allows certain things to be done with mutable structs which it shouldn't, and doesn't conveniently allow some things that it should, but POD ("Plain Old Data") structs which have no mutating methods, but instead expose their entire state via public fields, have a very useful consistency in their behavior which is not shared with any other data type. Using a POD struct may confuse someone who isn't familiar with how they work, but will make the program much more readable by anyone who does.

Consider, for example, the following code, assuming EmployeeInfoStruct contains nothing but value types and immutable class types like String:

[employeeInfoStruct is a struct containing the following field]
public Decimal YearlyBonus;

[someEmployeeContainer is an instance of a class which includes the following method]
EmployeeInfoStruct GetEmployeeInfo(String id);  // Just the signature--code is immaterial

[some other method uses the following code]
EmployeeInfoStruct anEmployee = someEmployeeContainer.GetEmployeeInfo("123-45-6789");
anEmployee.YearlyBonus += 100;

Eric Lippert complains that the above code will alter the value in anEmployee, but that change won't have any effect on the container. I would suggest that's a good thing--anyone who knows how structs work could look at the above code and know writes to a struct variable will affect that variable, but won't affect anything else unless the program later uses some other method (perhaps SetEmployeeInfo) to store that variable someplace.

Now replace EmployeeInfoStruct with EmployeeInfoClass, which has a read/write property of type YearlyBonus. Using just the information above, what can one say about the the relationship between writes to someEmployeeContainer and anEmployee? Depending upon the implementations of anEmployee's class (which, unless EmployeeInfoClass is sealed, might or might not actually be EmployeeInfoClass) and someEmployeeContainer, the relationship between the objects could be anything. Writes to one might:

  1. Have no effect on the other
  2. Update the other in 'natural' fashion
  3. Corrupt the other in some arbitrary way

With structs containing nothing but fields of either value types or immutable classes, the semantics are always going to be #1. One doesn't have to look at the code for the struct itself, nor the code of the container, to know that. By contrast, if the anEmployee.Salary or someEmployeeContainer.GetEmployee is virtual, it's impossible to really know what the semantics will be.

It's important to note that, if structs are large, passing them by value or returning them from functions can be expensive. It's generally better to pass large structs as ref parameters when possible. Although the built-in collections really don't do a good job of facilitating such usage, it can make using a hundreds-of-bytes struct cheaper than using a class.

supercat
  • 69,493
  • 7
  • 143
  • 184
3

The comment about structs being immutable is correct. And this is where it can bite you. You can define structs with field setters, but when you change a field value a new instance is created. So if you hold a reference to the old object it will still reference the old value. I don't like using mutable stucts for this reason as this can produce subtle and complex bugs (especially if you use complex compound statements).

On the other hand, there are lots of good reasons for using classes with immutable state also (think string).

  • 3
    *"when you change a field value a new instance is created"* - this line is a bit misleading IMO. For mutable structs (i.e. with field setters), you will simply change the field. Any part of code pointing to **that** struct will have it changed. For immutable structs, you will have to explicitly provide a way to construct a new instance with a different field value. – Groo Dec 23 '09 at 09:04
2

I remember one advice given on MSDN that struct should not be larget than 16 or 21 bytes. Looking for the link, but can't find it yet.

The main implication was that once you have a string in your data type - make it a class without thinking. Otherwise the struct shouldn't hold much.

Tomas Pajonk
  • 4,972
  • 8
  • 38
  • 51
  • String is a class, so its content is in the heap, not in the stack. If you have a string as a field of a struct, it occupies only 4 bytes in this struct (a pointer). – VladV Dec 23 '09 at 08:34
  • 1
    The code to handle value types employs special optimizations when they are no larger than 16 bytes. Using a 17-byte struct may thus be considerably slower than using a 16-byte struct. On the other hand, some struct operations are so much cheaper than class operations that even a hundred-byte struct may be more efficient than a hundred-byte class (some struct operations are faster than class operations while others are slower; whether a struct or class would perform faster overall would depend upon the mix of operations performed). – supercat Aug 10 '12 at 21:07
2

I think you have the right idea. Structs are made to mimic data-types. They are value driven not reference based. If you look at the MSDN documentation for most of the base data classes (int, double, decimal, ect.) they are all based on structs. That being said however, structs should not be overused for that very same reason. Room to store all everything in that struct is allocated as soon as it is instantiated, where as classes just allocate room for a reference to everything inside. If the data is in small enough chunks where this is not a problem than structs are the way to go. If this is an issue go with classes. If you don't know than it might just be best to stick with what you are familiar with.

Christopher B. Adkins
  • 3,361
  • 2
  • 23
  • 27
0

If you have low latency requirements and A LOT of objects slow garbage collections can be a problem. In that case struct can be very helpful because the garbage collector does not need to scan through a hierarchy of value types if the value types does not contain any reference types.

You can find a benchmark here: http://00sharp.wordpress.com/2013/07/03/a-case-for-the-struct/

Rabbit
  • 1,621
  • 1
  • 17
  • 27