336

MSDN says that you should use structs when you need lightweight objects. Are there any other scenarios when a struct is preferable over a class?

Some people might have forgotten that:

  1. structs can have methods.
  2. structs cannot be inherited.

I understand the technical differences between structs and classes, I just don't have a good feel for when to use a struct.

AbyxDev
  • 1,363
  • 16
  • 30
Esteban Araya
  • 29,284
  • 24
  • 107
  • 141
  • Just a reminder - what most people tend to forget in this context is that in C# structs can have methods as well. – petr k. Sep 17 '08 at 17:24

16 Answers16

319

MSDN has the answer: Choosing Between Classes and Structures.

Basically, that page gives you a 4-item checklist and says to use a class unless your type meets all of the criteria.

Do not define a structure unless the type has all of the following characteristics:

  • It logically represents a single value, similar to primitive types (integer, double, and so on).
  • It has an instance size smaller than 16 bytes.
  • It is immutable.
  • It will not have to be boxed frequently.
Jordan S. Jones
  • 13,703
  • 5
  • 44
  • 49
OwenP
  • 24,950
  • 13
  • 65
  • 102
  • 1
    Maybe I'm missing something obvious but I don't quite get the reasoning behind the "immutable" part. Why is this necessary? Could someone explain it? – Tamas Czinege Jan 22 '09 at 19:11
  • 3
    They've probably recommended this because if the struct is immutable, then it won't matter that it has value semantics rather than reference semantics. The distinction only matters if you mutate the object/struct after making a copy. – Stephen C. Steel Mar 26 '10 at 22:07
  • @DrJokepu: In some situations, the system will make a temporary copy of a struct and then allow that copy to be passed by reference to code that changes it; since the temporary copy will be jettisoned, the change will be lost. This problem is especially severe if a struct has methods that mutate it. I emphatically disagree with the notion that mutability is a reason to make something a class, since--in spite of some deficiencies in c# and vb.net, mutable structs provide useful semantics that cannot be achieved any other way; there is no semantic reason to prefer an immutable struct to a class. – supercat Jan 20 '11 at 21:23
  • I prefer Scott Meyers' principle. He explicitly deviates from the .NET documentation because he believes the use of the type is a better factor than the size of a type. – Bart Gijssens Jun 15 '11 at 09:59
  • I am wondering, does the 16 byte size apply to 64-bit code? I am not 100% sure where the 16 byte number comes from. – Chuu Feb 27 '12 at 18:57
  • 5
    @Chuu: In designing the JIT compiler, Microsoft decided to optimize the code for copying structs which are 16 bytes or smaller; this means that copying a 17-byte struct may be significantly slower than copying a 16-byte struct. I see no particular reason to expect Microsoft to extend such optimizations to larger structs, but it's important to note that while 17-byte structurs may be slower to copy than 16-byte structs, there are many cases where large structs may be more efficient than large class objects, and where the relative advantage of structs *grows* with the size of the struct. – supercat Jan 02 '13 at 16:12
  • 3
    @Chuu: Applying the same usage patterns to large structures as one would to classes is apt to result in inefficient code, but the proper solution often not to replace the structs with classes, but instead to use structs more efficiently; most notably, one should avoid passing or returning structs by value. Pass them as `ref` parameters whenever it's reasonable to do so. Pass a struct with 4,000 fields as a ref parameter to a method which changes one will be cheaper than passing a struct with 4 fields by value to a method which returns modified version. – supercat Jan 02 '13 at 16:18
  • @supercat - wait - what are the *many cases* where a large struct is more efficient than a large class instance? I can only see that being possible if it was necessary to enforce immutability, so the "class" approach ended up having to create a new instance any time you did anything... I've never encountered that, but would be interested to know when it occurs. – ToolmakerSteve Apr 02 '18 at 19:10
  • I know this is an old post but ```structs``` are recommended to be immutable because they are *typically (remember interface referencing)* value type objects and values are copied rather than updated. Although you can point into a struct and change an existing value; the struct is still copied not updated in place like a class. In other words; it's safer and more inline with how existing structs work ie. (```int, char, DateTime```, ETC) – Michael Puckett II Dec 21 '18 at 13:00
58

I am surprised I have not read at any of the previous answer this, which I consider the most crucial aspect :

I use structs when I want a type with no identity. For example a 3D point:

public struct ThreeDimensionalPoint
{
    public readonly int X, Y, Z;
    public ThreeDimensionalPoint(int x, int y, int z)
    {
        this.X = x;
        this.Y = y;
        this.Z = z;
    }

    public override string ToString()
    {
        return "(X=" + this.X + ", Y=" + this.Y + ", Z=" + this.Z + ")";
    }

    public override int GetHashCode()
    {
        return (this.X + 2) ^ (this.Y + 2) ^ (this.Z + 2);
    }

    public override bool Equals(object obj)
    {
        if (!(obj is ThreeDimensionalPoint))
            return false;
        ThreeDimensionalPoint other = (ThreeDimensionalPoint)obj;
        return this == other;
    }

    public static bool operator ==(ThreeDimensionalPoint p1, ThreeDimensionalPoint p2)
    {
        return p1.X == p2.X && p1.Y == p2.Y && p1.Z == p2.Z;
    }

    public static bool operator !=(ThreeDimensionalPoint p1, ThreeDimensionalPoint p2)
    {
        return !(p1 == p2);
    }
}

If you have two instances of this struct you don't care if they are a single piece of data in memory or two. You just care about the value(s) they hold.

Andrei Rînea
  • 20,288
  • 17
  • 117
  • 166
  • An interesting reason to use a struct. I have made classes with GetHashCode and Equals defined similar to what you show here, but then i always had to be careful not to mutate those instances if I used them as dictionary keys. Probably would have been safer if I had defined them as structs. (Because then the key would be a *copy* of the fields *at the moment the struct became a dictionary key*, so key would remain unchanged if I later changed the original.) – ToolmakerSteve Apr 02 '18 at 19:25
  • In your example it's ok because you only have 12 Bytes but bear in mind that if you have lots of fields in that struct that overpass 16 Bytes, you must consider to use a class and override GetHashCode and Equals methods. – Daniel Botero Correa Feb 21 '19 at 11:31
  • A value type in DDD does not mean you should necessarily use a value type in C# – Darragh Apr 04 '19 at 17:31
32

Bill Wagner has a chapter about this in his book "effective c#" (http://www.amazon.com/Effective-Specific-Ways-Improve-Your/dp/0321245660). He concludes by using the following principle:

  1. Is the main responsability of the type data storage?
  2. Is its public interface defined entirely by properties that access or modify its data members?
  3. Are you sure your type will never have subclasses?
  4. Are you sure your type will never be treated polymorphically?

If you answer 'yes' to all 4 questions: use a struct. Otherwise, use a class.

Liam
  • 27,717
  • 28
  • 128
  • 190
Bart Gijssens
  • 1,572
  • 5
  • 20
  • 38
  • 2
    so...data transfer objects (DTOs) should be structs? – cruizer Aug 02 '11 at 08:34
  • 1
    I'd say yes, if it meets the 4 criteria described above. Why would a data transfer object need to be treated in a specific way? – Bart Gijssens Aug 02 '11 at 09:23
  • 2
    @cruizer depends on your situation. On one project, we had common audit fields in our DTOs and hence wrote a base DTO which others inherited from. – Andrew Grothe Mar 04 '13 at 18:52
  • based on the definition DTO can be structs if you are not inheriting something from a base class (which is commonly done with DTOs) – Juan Zamora Mar 22 '18 at 13:20
  • 1
    All but (2) seem like excellent principles. Would need to see his reasoning to know what exactly he means by (2), and why. – ToolmakerSteve Apr 02 '18 at 19:32
  • 1
    @ToolmakerSteve: You will have to read the book for that. Don't think it's fair to copy/paste large portions of a book. – Bart Gijssens Apr 03 '18 at 11:20
20

Use a class if:

  • Its identity is important. Structures get copied implicitly when being passed by value into a method.
  • It will have a large memory footprint.
  • Its fields need initializers.
  • You need to inherit from a base class.
  • You need polymorphic behavior;

Use a structure if:

  • It will act like a primitive type (int, long, byte, etc.).
  • It must have a small memory footprint.
  • You are calling a P/Invoke method that requires a structure to be passed in by value.
  • You need to reduce the impact of garbage collection on application performance.
  • Its fields need to be initialized only to their default values. This value would be zero for numeric types, false for Boolean types, and null for reference types.
    • Note that in C# 6.0 structs can have a default constructor that can be used to initialize the struct’s fields to nondefault values.
  • You do not need to inherit from a base class (other than ValueType, from which all structs inherit).
  • You do not need polymorphic behavior.
Regular Jo
  • 5,190
  • 3
  • 25
  • 47
Yashwanth Kata
  • 817
  • 8
  • 21
17

Use a struct when you want value-type semantics instead of reference-type. Structs are copy-by-value so be careful!

Also see previous questions, e.g.

What's the difference between struct and class in .NET?

Community
  • 1
  • 1
Simon Steele
  • 11,558
  • 4
  • 45
  • 67
14

I would use structs when:

  1. an object is supposed to be read only(every time you pass/assign a struct it gets copied). Read only objects are great when it comes to multithreaded processing as they don't requite locking in most cases.

  2. an object is small and short-living. In such a case there is a good chance that the object will be allocated on the stack which is much more efficient than putting it on the managed heap. What is more the memory allocated by the object will be freed as soon as it goes outside its scope. In other words it's less work for Garbage Collector and the memory is used more efficient.

Pawel Pabich
  • 2,404
  • 4
  • 22
  • 33
9

It is an old topic, but wanted to provide a simple benchmark test.

I have created two .cs files:

public class TestClass
{
    public long ID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

and

public struct TestStruct
{
    public long ID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

Run benchmark:

  • Create 1 TestClass
  • Create 1 TestStruct
  • Create 100 TestClass
  • Create 100 TestStruct
  • Create 10000 TestClass
  • Create 10000 TestStruct

Results:

BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18362
Intel Core i5-8250U CPU 1.60GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=3.1.101
[Host]     : .NET Core 3.1.1 (CoreCLR 4.700.19.60701, CoreFX 4.700.19.60801), X64 RyuJIT  [AttachedDebugger]
DefaultJob : .NET Core 3.1.1 (CoreCLR 4.700.19.60701, CoreFX 4.700.19.60801), X64 RyuJIT


|         Method |           Mean |         Error |        StdDev |     Ratio | RatioSD | Rank |    Gen 0 | Gen 1 | Gen 2 | Allocated |
|--------------- |---------------:|--------------:|--------------:|----------:|--------:|-----:|---------:|------:|------:|----------:|

|      UseStruct |      0.0000 ns |     0.0000 ns |     0.0000 ns |     0.000 |    0.00 |    1 |        - |     - |     - |         - |
|       UseClass |      8.1425 ns |     0.1873 ns |     0.1839 ns |     1.000 |    0.00 |    2 |   0.0127 |     - |     - |      40 B |
|   Use100Struct |     36.9359 ns |     0.4026 ns |     0.3569 ns |     4.548 |    0.12 |    3 |        - |     - |     - |         - |
|    Use100Class |    759.3495 ns |    14.8029 ns |    17.0471 ns |    93.144 |    3.24 |    4 |   1.2751 |     - |     - |    4000 B |
| Use10000Struct |  3,002.1976 ns |    25.4853 ns |    22.5920 ns |   369.664 |    8.91 |    5 |        - |     - |     - |         - |
|  Use10000Class | 76,529.2751 ns | 1,570.9425 ns | 2,667.5795 ns | 9,440.182 |  346.76 |    6 | 127.4414 |     - |     - |  400000 B |
Eduardas Šlutas
  • 178
  • 3
  • 13
6

If an entity is going to be immutable, the question of whether to use a struct or a class will generally be one of performance rather than semantics. On a 32/64-bit system, class references require 4/8 bytes to store, regardless of the amount of information in the class; copying a class reference will require copying 4/8 bytes. On the other hand, every distinct class instance will have 8/16 bytes of overhead in addition to the information it holds and the memory cost of the references to it. Suppose one wants an array of 500 entities, each holding four 32-bit integers. If the entity is a structure type, the array will require 8,000 bytes regardless of whether all 500 entities are all identical, all different, or somewhere between. If the entity is a class type, the array of 500 references will take 4,000 bytes. If those references all point to different objects, the objects would require an additional 24 bytes each (12,000 bytes for all 500), a total of 16,000 bytes--twice the storage cost of a struct type. On the other hand, of the code created one object instance and then copied a reference to all 500 array slots, the total cost would be 24 bytes for that instance and 4,000 for the array--a total of 4,024 bytes. A major savings. Few situations would work out as well as the last one, but in some cases it may be possible to copy some references to enough array slots to make such sharing worthwhile.

If the entity is supposed to be mutable, the question of whether to use a class or struct is in some ways easier. Assume "Thing" is either a struct or class which has an integer field called x, and one does the following code:

  Thing t1,t2;
  ...
  t2 = t1;
  t2.x = 5;

Does one want the latter statement to affect t1.x?

If Thing is a class type, t1 and t2 will be equivalent, meaning t1.x and t2.x will also be equivalent. Thus, the second statement will affect t1.x. If Thing is a structure type, t1 and t2 will be different instances, meaning t1.x and t2.x will refer to different integers. Thus, the second statement will not affect t1.x.

Mutable structures and mutable classes have fundamentally different behaviors, though .net has some quirks in its handling of struct mutations. If one wants value-type behavior (meaning that "t2=t1" will copy the data from t1 to t2 while leaving t1 and t2 as distinct instances), and if one can live with the quirks in .net's handling of value types, use a structure. If one wants value-type semantics but .net's quirks would cause lead to broken value-type semantics in one's application, use a class and mumble.

supercat
  • 77,689
  • 9
  • 166
  • 211
6

I have always used a struct when I wanted to group together a few values for passing things back from a method call, but I won't need to use it for anything after I have read those values. Just as a way to keep things clean. I tend to view things in a struct as "throwaway" and things in a class as more useful and "functional"

Ryan Skarin
  • 3,087
  • 3
  • 21
  • 18
  • Keeping things "clean" in design principals would mean you don't arbitrarily return more than one value from a function. Anticipating what the caller wants is an antipattern. – Maxx Sep 26 '21 at 22:22
4

In addition the the excellent answers above:

Structures are value types.

They can never be set to Nothing.

Setting a structure = Nothing , will set all its values types to their default values.

George Filippakos
  • 16,359
  • 15
  • 81
  • 92
3

✔️ CONSIDER defining a struct instead of a class if instances of the type are small and commonly short-lived or are commonly embedded in other objects.

3

when you don't really need behavior, but you need more structure than a simple array or dictionary.

Follow up This is how I think of structs in general. I know they can have methods, but I like keeping that overall mental distinction.

Jim Deville
  • 10,632
  • 1
  • 37
  • 47
3

As @Simon said, structs provide "value-type" semantics so if you need similar behavior to a built-in data type, use a struct. Since structs are passed by copy you want to make sure they are small in size, about 16 bytes.

Scott Dorman
  • 42,236
  • 12
  • 79
  • 110
2

Structs are on the Stack not the Heap so therefore they are thread safe, and should be used when implementing the transfer object pattern, you never want to use objects on the Heap they are volatile, you want in this case to use the Call Stack, this is a basic case for using a struct I am surprised by all the way out answers here,

Jack
  • 21
  • 1
2

Hmm...

I wouldn't use garbage collection as an argument for/against the use of structs vs classes. The managed heap works much like a stack - creating an object just puts it at the top of the heap, which is almost as fast as allocating on the stack. Additionally, if an object is short-lived and does not survive a GC cycle, deallocation is free as the GC only works with memory that's still accessible. (Search MSDN, there's a series of articles on .NET memory management, I'm just too lazy to go dig for them).

Most of the time I use a struct, I wind up kicking myself for doing so, because I later discover that having reference semantics would have made things a bit simpler.

Anyway, those four points in the MSDN article posted above seems a good guideline.

  • 1
    If you sometimes need reference semantics with a struct, simply declare `class MutableHolder { public T Value; MutableHolder(T value) {Value = value;} }`, and then a `MutableHolder` will be an object with mutable class semantics (this works just as well if `T` is a struct or an immutable class type). – supercat Feb 02 '13 at 00:08
-2

I think the best answer is simply to use struct when what you need is a collection of properties, class when it's a collection of properties AND behaviors.