ChatGPT解决这个技术问题 Extra ChatGPT

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

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

Good quesstion, it helped me as well.

L
Lasse V. Karlsen

In .NET, there are two categories of types, reference types and value types.

Structs are value types and classes are reference types.

The general difference is that a reference type lives on the heap, and a value type lives inline, that is, wherever it is your variable or field is defined.

A variable containing a value type contains the entire value type value. For a struct, that means that the variable contains the entire struct, with all its fields.

A variable containing a reference type contains a pointer, or a reference to somewhere else in memory where the actual value resides.

This has one benefit, to begin with:

value types always contains a value

reference types can contain a null-reference, meaning that they don't refer to anything at all at the moment

Internally, reference types are implemented as pointers, and knowing that, and knowing how variable assignment works, there are other behavioral patterns:

copying the contents of a value type variable into another variable, copies the entire contents into the new variable, making the two distinct. In other words, after the copy, changes to one won't affect the other

copying the contents of a reference type variable into another variable, copies the reference, which means you now have two references to the same somewhere else storage of the actual data. In other words, after the copy, changing the data in one reference will appear to affect the other as well, but only because you're really just looking at the same data both places

When you declare variables or fields, here's how the two types differ:

variable: value type lives on the stack, reference type lives on the stack as a pointer to somewhere in heap memory where the actual memory lives (though note Eric Lipperts article series: The Stack Is An Implementation Detail.)

class/struct-field: value type lives completely inside the type, reference type lives inside the type as a pointer to somewhere in heap memory where the actual memory lives.


In the interest of full completeness, I should mention that Eric Lippert has said that the stack is an implementation detail, whenever I mention stack above, have Eric's post(s) in mind.
Is this all valid for C++ as well?
another crucial difference is usage. From MSDN: "structs are typically used to encapsulate small group of related variables, such as coordinates of rectangle. Structs can also contain constructors, constants, fields, methods, properties, indexers, operators, events, and nested types, although if several such members are required, you should consider making your type a class instead."
@KorayTugay in C++ struct and class are absolutely equivalent except for the one thing - default access restriction (class has private by default, struct has public)
@KorayTugay In terms of "value types vs. reference types", no, C++ is not equivalent to C# in this regard. In C++ you can create an instance of a class on the stack (vs. a reference to an instance allocated on the heap). As berkus said, in C++, classes and structs are equivalent, except for default access. Both can act as value types, or can be referenced by pointer or reference.
T
Thomas Bratt

A short summary of each:

Classes Only:

Can support inheritance

Are reference (pointer) types

The reference can be null

Have memory overhead per new instance

Structs Only:

Cannot support inheritance

Are value types

Are passed by value (like integers)

Cannot have a null reference (unless Nullable is used)

Do not have a memory overhead per new instance - unless 'boxed'

Both Classes and Structs:

Are compound data types typically used to contain a few variables that have some logical relationship

Can contain methods and events

Can support interfaces


There are some parts of this answer that are not quite right. Classes do not always go on the heap, and structs do not always go on the stack. Current exceptions include struct fields on a class, captured variables in anonymous methods and lambda expressions, iterator blocks, and the already mentioned boxed values. But stack vs heap allocation is a implementation detail and may be subject to change. Eric lippart discusses this here. I've downvoted, but will happily remove it if you update.
struct do not support inheritance from other stucts/classes, but you CAN implement an interface on a struct.
You might want to clarify what you mean when you claim that structs "Do not have a memory overhead per new instance". My first interpretation was that you were claiming - obviously absurdly - that structs use zero memory. Then I thought that maybe you're trying to say that a struct, unlike a class, requires exactly as much memory as the sum of its member fields, and no more. But then I Googled for c# struct memory overhead and found this answer by Hans Passant that says that no, that's not the case either. So what do you mean?
@MarkAmery I had the same initial reaction as you id to the expression "no memory overhead", but I think that the OP is referring to the fact that instances of class are managed memory (handled by the garbage collector), whereas instances of struct are not.
"Struct Are passed by value (like integers)" is false: all variable are passed by value, also the reference type. If you want to pass a variable by reference you have to use the "ref" keyword. jonskeet.uk/csharp/parameters.html#ref
M
Matt Ellen

Difference between Structs and Classes:

Struct are value types whereas Classes are reference types.

Structs are stored on the stack whereas Classes are stored on the heap.

Value types hold their value in memory where they are declared, but a reference type holds a reference to an object in memory.

Value types are destroyed immediately after the scope is lost whereas reference type only destroys the variable after the scope is lost. The object is later destroyed by the garbage collector.

When you copy a struct into another struct, a new copy of that struct gets created. The modified struct won't affect the value of the other struct.

When you copy a class into another class, it only copies the reference variable.

Both reference variables point to the same object on the heap. Changes done to one variable will affect the other reference variable.

Structs can not have destructors, but classes can have destructors.

Structs can not have explicit parameter-less constructors whereas classes can. Structs don't support inheritance, but classes do. Both support inheritance from an interface.

Structs are sealed type.


I find this an exhaustive and concise answer. Want to know more about each bullet? Go learn.
It's not necessarily true that "structs are stored on the stack whereas classes are stored on the heap." See stackoverflow.com/questions/3542083/…
P
Peter Mortensen

In .NET the struct and class declarations differentiate between reference types and value types.

When you pass round a reference type there is only one actually stored. All the code that accesses the instance is accessing the same one.

When you pass round a value type each one is a copy. All the code is working on its own copy.

This can be shown with an example:

struct MyStruct 
{
    string MyProperty { get; set; }
}

void ChangeMyStruct(MyStruct input) 
{ 
   input.MyProperty = "new value";
}

...

// Create value type
MyStruct testStruct = new MyStruct { MyProperty = "initial value" }; 

ChangeMyStruct(testStruct);

// Value of testStruct.MyProperty is still "initial value"
// - the method changed a new copy of the structure.

For a class this would be different

class MyClass 
{
    string MyProperty { get; set; }
}

void ChangeMyClass(MyClass input) 
{ 
   input.MyProperty = "new value";
}

...

// Create reference type
MyClass testClass = new MyClass { MyProperty = "initial value" };

ChangeMyClass(testClass);

// Value of testClass.MyProperty is now "new value" 
// - the method changed the instance passed.

Classes can be nothing - the reference can point to a null.

Structs are the actual value - they can be empty but never null. For this reason structs always have a default constructor with no parameters - they need a 'starting value'.


@T.Todua yeah, there are better answers above, that I voted up and picked as the answer after providing this one - this is from the early beta of SO when we were still figuring out the rules.
I don't know if you correctly understood me, I really upvoted/accepted your answer (as opposed to above answers), because your's had good examples (not only theorical explanation, as opposed to above answer, which had only theoretical explanations without examples).
S
SunsetQuest

From Microsoft's Choosing Between Class and Struct ...

As a rule of thumb, the majority of types in a framework should be classes. There are, however, some situations in which the characteristics of a value type make it more appropriate to use structs. ✓ CONSIDER a struct instead of a class: If instances of the type are small and commonly short-lived or are commonly embedded in other objects. X AVOID a struct unless the type has all of the following characteristics: It logically represents a single value, similar to primitive types (int, double, etc.). It has an instance size under 16 bytes. It is immutable. (cannot be changed) It will not have to be boxed frequently.


This should be the concise answer.
A
Arsen Khachaturyan

https://i.stack.imgur.com/M9c18.png

And the textual representation just in case ;)

+--------------------------------------------------+------+----------------------------------------------+
|                      Struct                      |      |                      Class                    |
+--------------------------------------------------+------+----------------------------------------------+
| - 1 per Thread.                                  |      | - 1 per application.                         |
|                                                  |      |                                              |
| - Holds value types.                             |      | - Holds reference types.                     |
|                                                  |      |                                              |
| - Types in the stack are positioned              |      | - No type ordering (data is fragmented).     |
|   using the LIFO principle.                      |      |                                              |
|                                                  |      |                                              |
| - Can't have a default constructor and/or        |      | - Can have a default constructor             |
|   finalizer(destructor).                         |      |   and/or finalizer.                          |
|                                                  |      |                                              |
| - Can be created with or without a new operator. |      | - Can be created only with a new operator.   |
|                                                  |      |                                              |
| - Can't derive from the class or struct          |  VS  | - Can have only one base class and/or        |
|   but can derive from the multiple interfaces.   |      |   derive from multiple interfaces.           |
|                                                  |      |                                              |
| - The data members can't be protected.           |      | - Data members can be protected.             |
|                                                  |      |                                              |
| - Function members can't be                      |      | - Function members can be                    |
|   virtual or abstract.                           |      |   virtual or abstract.                       |
|                                                  |      |                                              |
| - Can't have a null value.                       |      | - Can have a null value.                     |
|                                                  |      |                                              |
| - During an assignment, the contents are         |      | - Assignment is happening                    |
|   copied from one variable to another.           |      |   by reference.                              |
+--------------------------------------------------+------+----------------------------------------------+

For more information look below:

Classes and structs (official documentation).

Choosing Between Class and Struct (official documentation).


M
Mark Amery

In addition to all differences described in the other answers:

Structs cannot have an explicit parameterless constructor whereas a class can Structs cannot have destructors, whereas a class can Structs can't inherit from another struct or class whereas a class can inherit from another class. (Both structs and classes can implement from an interface.)

If you are after a video explaining all the differences, you can check out Part 29 - C# Tutorial - Difference between classes and structs in C#.


Much more significant than the fact that .net languages will generally not allow a struct to define a parameterless constructor (the decision of whether or not to allow it is made by the language compiler) is the fact that structures can come into existence and be exposed to the outside world without any sort of constructor having been run (even when a parameterless constructor is defined). The reason .net languages generally forbid parameterless constructors for structs is to avoid the confusion that would result from having such constructors be sometimes run and sometimes not.
A
Avestura

Struct Class Type Value-type Reference-type Where On stack / Inline in containing type On Heap Deallocation Stack unwinds / containing type gets deallocated Garbage Collected Arrays Inline, elements are the actual instances of the value type Out of line, elements are just references to instances of the reference type residing on the heap Al-Del Cost Cheap allocation-deallocation Expensive allocation-deallocation Memory usage Boxed when cast to a reference type or one of the interfaces they implement, Unboxed when cast back to value type (Negative impact because boxes are objects that are allocated on the heap and are garbage-collected) No boxing-unboxing Assignments Copy entire data Copy the reference Change to an instance Does not affect any of its copies Affect all references pointing to the instance Mutability Should be immutable Mutable Population In some situations Majority of types in a framework should be classes Lifetime Short-lived Long-lived Destructor Cannot have Can have Inheritance Only from an interface Full support Polymorphism No Yes Sealed Yes When have sealed keyword (C#), or Sealed attribute (F#) Constructor Can not have explicit parameterless constructors Any constructor Null-assignments When marked with nullable question mark Yes (When marked with nullable question mark in C# 8+ and F# 5+ 1) Abstract No When have abstract keyword (C#), or AbstractClass attribute (F#) Member Access Modifiers public, private, internal public, protected, internal, protected internal, private protected

1 Using null is not encouraged in F#, use Option type instead.


This is actually quite splendid: summarised and informative. Please only remember to proof-read your answer at least once - you swapped struct and class explanations in some rows, also there are some typos.
@ensisNoctis Sorry for those mistakes and thanks for the edit. I should re-read my answers 😅
Z
Zooba

Instances of classes are stored on the managed heap. All variables 'containing' an instance are simply a reference to the instance on the heap. Passing an object to a method results in a copy of the reference being passed, not the object itself.

Structures (technically, value types) are stored wherever they are used, much like a primitive type. The contents may be copied by the runtime at any time and without invoking a customised copy-constructor. Passing a value type to a method involves copying the entire value, again without invoking any customisable code.

The distinction is made better by the C++/CLI names: "ref class" is a class as described first, "value class" is a class as described second. The keywords "class" and "struct" as used by C# are simply something that must be learned.


P
Peter Mortensen

Structure vs Class

A structure is a value type so it is stored on the stack, but a class is a reference type and is stored on the heap.

A structure doesn't support inheritance, and polymorphism, but a class supports both.

By default, all the struct members are public but class members are by default private in nature.

As a structure is a value type, we can't assign null to a struct object, but it is not the case for a class.


Regarding "all the struct members are public": If I'm not mistaken, that is incorrect. "The access level for class members and struct members, including nested classes and structs, is private by default." msdn.microsoft.com/en-us/library/ms173121.aspx
" we can't assign null to a struct object" is not true as you can use the Nullable type with structs.
W
Will Calderwood

To add to the other answers, there is one fundamental difference that is worth noting, and that is how the data is stored within arrays as this can have a major effect on performance.

With a struct, the array contains the instance of the struct

With a class, the array contains a pointer to an instance of the class elsewhere in memory

So an array of structs looks like this in memory

[struct][struct][struct][struct][struct][struct][struct][struct]

Whereas an array of classes looks like this

[pointer][pointer][pointer][pointer][pointer][pointer][pointer][pointer]

With an array of classes, the values you're interested in are not stored within the array, but elsewhere in memory.

For a vast majority of applications this difference doesn't really matter, however, in high performance code this will affect locality of data within memory and have a large impact on the performance of the CPU cache. Using classes when you could/should have used structs will massively increase the number of cache misses on the CPU.

The slowest thing a modern CPU does is not crunching numbers, it's fetching data from memory, and an L1 cache hit is many times faster than reading data from RAM.

Here's some code you can test. On my machine, iterating through the class array takes ~3x longer than the struct array.

    private struct PerformanceStruct
    {
        public int i1;
        public int i2;
    }

    private class PerformanceClass
    {
        public int i1;
        public int i2;
    }

    private static void DoTest()
    {
        var structArray = new PerformanceStruct[100000000];
        var classArray = new PerformanceClass[structArray.Length];

        for (var i = 0; i < structArray.Length; i++)
        {
            structArray[i] = new PerformanceStruct();
            classArray[i] = new PerformanceClass();
        }

        long total = 0;
        var sw = new Stopwatch();
        sw.Start();
        for (var loops = 0; loops < 100; loops++)
        for (var i = 0; i < structArray.Length; i++)
        {
            total += structArray[i].i1 + structArray[i].i2;
        }

        sw.Stop();
        Console.WriteLine($"Struct Time: {sw.ElapsedMilliseconds}");
        sw = new Stopwatch();
        sw.Start();
        for (var loops = 0; loops < 100; loops++)
        for (var i = 0; i < classArray.Length; i++)
        {
            total += classArray[i].i1 + classArray[i].i2;
        }

        Console.WriteLine($"Class Time: {sw.ElapsedMilliseconds}");
    }

-1; "Structs are value types, so they store a value, classes are reference types, so they reference a class." is unclear and unlikely to make sense to anybody who hasn't already understood it from the other answers here, and "With a class the containing class will just contain a pointer to the new class in a different area of memory." confuses classes with class instances.
@MarkAmery I've tried to clarify slightly. The point I was really trying to make was the difference in the ways that arrays work with value and reference types and the effect this has on performance. I wasn't trying to re-explain what value and reference types are as this is done in plenty of other answers.
E
Ed S.

Well, for starters, a struct is passed by value rather than by reference. Structs are good for relatively simple data structures, while classes have a lot more flexibility from an architectural point of view via polymorphism and inheritance.

Others can probably give you more detail than I, but I use structs when the structure that I am going for is simple.


Class objects are also passed by value by default
@Imad: value of the reference, yes
M
Mark Amery

Just to make it complete, there is another difference when using the Equals method, which is inherited by all classes and structures.

Lets's say we have a class and a structure:

class A{
  public int a, b;
}
struct B{
  public int a, b;
}

and in the Main method, we have 4 objects.

static void Main{
  A c1 = new A(), c2 = new A();
  c1.a = c1.b = c2.a = c2.b = 1;
  B s1 = new B(), s2 = new B();
  s1.a = s1.b = s2.a = s2.b = 1;
}

Then:

s1.Equals(s2) // true
s1.Equals(c1) // false
c1.Equals(c2) // false
c1 == c2 // false

So, structures are suited for numeric-like objects, like points (save x and y coordinates). And classes are suited for others. Even if 2 people have same name, height, weight..., they are still 2 people.


P
Peter Mortensen

Events declared in a class have their += and -= access automatically locked via a lock(this) to make them thread safe (static events are locked on the typeof the class). Events declared in a struct do not have their += and -= access automatically locked. A lock(this) for a struct would not work since you can only lock on a reference type expression. Creating a struct instance cannot cause a garbage collection (unless the constructor directly or indirectly creates a reference type instance) whereas creating a reference type instance can cause garbage collection. A struct always has a built-in public default constructor. class DefaultConstructor { static void Eg() { Direct yes = new Direct(); // Always compiles OK InDirect maybe = new InDirect(); // Compiles if constructor exists and is accessible //... } } This means that a struct is always instantiable whereas a class might not be since all its constructors could be private. class NonInstantiable { private NonInstantiable() // OK { } } struct Direct { private Direct() // Compile-time error { } } A struct cannot have a destructor. A destructor is just an override of object.Finalize in disguise, and structs, being value types, are not subject to garbage collection. struct Direct { ~Direct() {} // Compile-time error } class InDirect { ~InDirect() {} // Compiles OK } And the CIL for ~Indirect() looks like this: .method family hidebysig virtual instance void Finalize() cil managed { // ... } // end of method Indirect::Finalize A struct is implicitly sealed, a class isn't. A struct can't be abstract, a class can. A struct can't call : base() in its constructor whereas a class with no explicit base class can. A struct can't extend another class, a class can. A struct can't declare protected members (for example, fields, nested types) a class can. A struct can't declare abstract function members, an abstract class can. A struct can't declare virtual function members, a class can. A struct can't declare sealed function members, a class can. A struct can't declare override function members, a class can. The one exception to this rule is that a struct can override the virtual methods of System.Object, viz, Equals(), and GetHashCode(), and ToString().


In what circumstances would one use an event with a struct? I can imagine that a very carefully-written program could use events with a struct in a way that would work, but only if the struct was never copied or passed by value, in which case it might as well be a class.
@supercat Yeah, a non-static event in a struct would be very strange, and it will be useful only for mutable structs, and the event itself (if it is a "field-like" event) turns the struct into the "mutable" category and also introduces a field of reference type in the struct. Non-static events in structs must be evil.
@JeppeStigNielsen: The only pattern I could see where it would make sense for a struct to have an event would be if the purpose of the struct was to hold an immutable reference to a class object for which it behaved as a proxy. Auto-events would be totally useless in such a scenario, though; instead, subscribe and unsubscribe events would have to be relayed to the class behind the structure. I wish .NET had (or would make it possible to define) a 'cache-box" structure type with an initially-null hidden field of type Object, which would hold a reference to a boxed copy of the struct.
@JeppeStigNielsen: Structs outperform classes in many proxy usage scenarios; the biggest problem with using structs is that in cases where boxing ends up being necessary, it often ends up getting deferred to an inner loop. If there were a way to avoid having structures get boxed repeatedly, they would be better than classes in many more usage scenarios.
k
kb9

As previously mentioned: Classes are reference type while Structs are value types with all the consequences.

As a thumb of rule Framework Design Guidelines recommends using Structs instead of classes if:

It has an instance size under 16 bytes

It logically represents a single value, similar to primitive types (int, double, etc.)

It is immutable

It will not have to be boxed frequently


B
Brad Larson

Besides the basic difference of access specifier, and few mentioned above I would like to add some of the major differences including few of the mentioned above with a code sample with output, which will give a more clear idea of the reference and value

Structs:

Are value types and do not require heap allocation.

Memory allocation is different and is stored in stack

Useful for small data structures

Affect performance, when we pass value to method, we pass the entire data structure and all is passed to the stack.

Constructor simply returns the struct value itself (typically in a temporary location on the stack), and this value is then copied as necessary

The variables each have their own copy of the data, and it is not possible for operations on one to affect the other.

Do not support user-specified inheritance, and they implicitly inherit from type object

Class:

Reference Type value

Stored in Heap

Store a reference to a dynamically allocated object

Constructors are invoked with the new operator, but that does not allocate memory on the heap

Multiple variables may have a reference to the same object

It is possible for operations on one variable to affect the object referenced by the other variable

Code Sample

    static void Main(string[] args)
    {
        //Struct
        myStruct objStruct = new myStruct();
        objStruct.x = 10;
        Console.WriteLine("Initial value of Struct Object is: " + objStruct.x);
        Console.WriteLine();
        methodStruct(objStruct);
        Console.WriteLine();
        Console.WriteLine("After Method call value of Struct Object is: " + objStruct.x);
        Console.WriteLine();

        //Class
        myClass objClass = new myClass(10);
        Console.WriteLine("Initial value of Class Object is: " + objClass.x);
        Console.WriteLine();
        methodClass(objClass);
        Console.WriteLine();
        Console.WriteLine("After Method call value of Class Object is: " + objClass.x);
        Console.Read();
    }
    static void methodStruct(myStruct newStruct)
    {
        newStruct.x = 20;
        Console.WriteLine("Inside Struct Method");
        Console.WriteLine("Inside Method value of Struct Object is: " + newStruct.x);
    }
    static void methodClass(myClass newClass)
    {
        newClass.x = 20;
        Console.WriteLine("Inside Class Method");
        Console.WriteLine("Inside Method value of Class Object is: " + newClass.x);
    }
    public struct myStruct
    {
        public int x;
        public myStruct(int xCons)
        {
            this.x = xCons;
        }
    }
    public class myClass
    {
        public int x;
        public myClass(int xCons)
        {
            this.x = xCons;
        }
    }

Output

Initial value of Struct Object is: 10

Inside Struct Method Inside Method value of Struct Object is: 20

After Method call value of Struct Object is: 10

Initial value of Class Object is: 10

Inside Class Method Inside Method value of Class Object is: 20

After Method call value of Class Object is: 20

Here you can clearly see the difference between call by value and call by reference.


R
Roman Pokrovskij

There is one interesting case of "class vs struct" puzzle - situation when you need to return several results from the method: choose which to use. If you know the ValueTuple story - you know that ValueTuple (struct) was added because it should be more effective then Tuple (class). But what does it mean in numbers? Two tests: one is struct/class that have 2 fields, other with struct/class that have 8 fields (with dimension more then 4 - class should become more effective then struct in terms processor ticks, but of course GC load also should be considered).

P.S. Another benchmark for specific case 'sturct or class with collections' is there: https://stackoverflow.com/a/45276657/506147

BenchmarkDotNet=v0.10.10, OS=Windows 10 Redstone 2 [1703, Creators Update] (10.0.15063.726)
Processor=Intel Core i5-2500K CPU 3.30GHz (Sandy Bridge), ProcessorCount=4
Frequency=3233540 Hz, Resolution=309.2586 ns, Timer=TSC
.NET Core SDK=2.0.3
  [Host] : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT
  Clr    : .NET Framework 4.7 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.2115.0
  Core   : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT


            Method |  Job | Runtime |     Mean |     Error |    StdDev |      Min |      Max |   Median | Rank |  Gen 0 | Allocated |
------------------ |----- |-------- |---------:|----------:|----------:|---------:|---------:|---------:|-----:|-------:|----------:|
  TestStructReturn |  Clr |     Clr | 17.57 ns | 0.1960 ns | 0.1834 ns | 17.25 ns | 17.89 ns | 17.55 ns |    4 | 0.0127 |      40 B |
   TestClassReturn |  Clr |     Clr | 21.93 ns | 0.4554 ns | 0.5244 ns | 21.17 ns | 23.26 ns | 21.86 ns |    5 | 0.0229 |      72 B |
 TestStructReturn8 |  Clr |     Clr | 38.99 ns | 0.8302 ns | 1.4097 ns | 37.36 ns | 42.35 ns | 38.50 ns |    8 | 0.0127 |      40 B |
  TestClassReturn8 |  Clr |     Clr | 23.69 ns | 0.5373 ns | 0.6987 ns | 22.70 ns | 25.24 ns | 23.37 ns |    6 | 0.0305 |      96 B |
  TestStructReturn | Core |    Core | 12.28 ns | 0.1882 ns | 0.1760 ns | 11.92 ns | 12.57 ns | 12.30 ns |    1 | 0.0127 |      40 B |
   TestClassReturn | Core |    Core | 15.33 ns | 0.4343 ns | 0.4063 ns | 14.83 ns | 16.44 ns | 15.31 ns |    2 | 0.0229 |      72 B |
 TestStructReturn8 | Core |    Core | 34.11 ns | 0.7089 ns | 1.4954 ns | 31.52 ns | 36.81 ns | 34.03 ns |    7 | 0.0127 |      40 B |
  TestClassReturn8 | Core |    Core | 17.04 ns | 0.2299 ns | 0.2150 ns | 16.68 ns | 17.41 ns | 16.98 ns |    3 | 0.0305 |      96 B |

Code test:

using System;
using System.Text;
using System.Collections.Generic;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Attributes.Columns;
using BenchmarkDotNet.Attributes.Exporters;
using BenchmarkDotNet.Attributes.Jobs;
using DashboardCode.Routines.Json;

namespace Benchmark
{
    //[Config(typeof(MyManualConfig))]
    [RankColumn, MinColumn, MaxColumn, StdDevColumn, MedianColumn]
    [ClrJob, CoreJob]
    [HtmlExporter, MarkdownExporter]
    [MemoryDiagnoser]
    public class BenchmarkStructOrClass
    {
        static TestStruct testStruct = new TestStruct();
        static TestClass testClass = new TestClass();
        static TestStruct8 testStruct8 = new TestStruct8();
        static TestClass8 testClass8 = new TestClass8();
        [Benchmark]
        public void TestStructReturn()
        {
            testStruct.TestMethod();
        }

        [Benchmark]
        public void TestClassReturn()
        {
            testClass.TestMethod();
        }


        [Benchmark]
        public void TestStructReturn8()
        {
            testStruct8.TestMethod();
        }

        [Benchmark]
        public void TestClassReturn8()
        {
            testClass8.TestMethod();
        }

        public class TestStruct
        {
            public int Number = 5;
            public struct StructType<T>
            {
                public T Instance;
                public List<string> List;
            }

            public int TestMethod()
            {
                var s = Method1(1);
                return s.Instance;
            }

            private StructType<int> Method1(int i)
            {
                return Method2(++i);
            }

            private StructType<int> Method2(int i)
            {
                return Method3(++i);
            }

            private StructType<int> Method3(int i)
            {
                return Method4(++i);
            }

            private StructType<int> Method4(int i)
            {
                var x = new StructType<int>();
                x.List = new List<string>();
                x.Instance = ++i;
                return x;
            }
        }

        public class TestClass
        {
            public int Number = 5;
            public class ClassType<T>
            {
                public T Instance;
                public List<string> List;
            }

            public int TestMethod()
            {
                var s = Method1(1);
                return s.Instance;
            }

            private ClassType<int> Method1(int i)
            {
                return Method2(++i);
            }

            private ClassType<int> Method2(int i)
            {
                return Method3(++i);
            }

            private ClassType<int> Method3(int i)
            {
                return Method4(++i);
            }

            private ClassType<int> Method4(int i)
            {
                var x = new ClassType<int>();
                x.List = new List<string>();
                x.Instance = ++i;
                return x;
            }
        }

        public class TestStruct8
        {
            public int Number = 5;
            public struct StructType<T>
            {
                public T Instance1;
                public T Instance2;
                public T Instance3;
                public T Instance4;
                public T Instance5;
                public T Instance6;
                public T Instance7;
                public List<string> List;
            }

            public int TestMethod()
            {
                var s = Method1(1);
                return s.Instance1;
            }

            private StructType<int> Method1(int i)
            {
                return Method2(++i);
            }

            private StructType<int> Method2(int i)
            {
                return Method3(++i);
            }

            private StructType<int> Method3(int i)
            {
                return Method4(++i);
            }

            private StructType<int> Method4(int i)
            {
                var x = new StructType<int>();
                x.List = new List<string>();
                x.Instance1 = ++i;
                return x;
            }
        }

        public class TestClass8
        {
            public int Number = 5;
            public class ClassType<T>
            {
                public T Instance1;
                public T Instance2;
                public T Instance3;
                public T Instance4;
                public T Instance5;
                public T Instance6;
                public T Instance7;
                public List<string> List;
            }

            public int TestMethod()
            {
                var s = Method1(1);
                return s.Instance1;
            }

            private ClassType<int> Method1(int i)
            {
                return Method2(++i);
            }

            private ClassType<int> Method2(int i)
            {
                return Method3(++i);
            }

            private ClassType<int> Method3(int i)
            {
                return Method4(++i);
            }

            private ClassType<int> Method4(int i)
            {
                var x = new ClassType<int>();
                x.List = new List<string>();
                x.Instance1 = ++i;
                return x;
            }
        }
    }
}

d
denis phillips

Structs are the actual value - they can be empty but never null

This is true, however also note that as of .NET 2 structs support a Nullable version and C# supplies some syntactic sugar to make it easier to use.

int? value = null;
value  = 1;

Be aware that this is only syntactic sugar which reads 'Nullable value = null;'
@ErikvanBrakel That's not just syntactic sugar. The different boxing rules mean (object)(default(int?)) == null which you can't do with any other value type, because there's more than just sugar going on here. The only sugar is int? for Nullable<int>.
-1; this doesn't address the question of what the difference between structs and classes is, and as such, it should've been a comment on the answer you're replying to, not a separate answer. (Although perhaps the site norms were different back in August 2008!)
P
Peter Mortensen

Every variable or field of a primitive value type or structure type holds a unique instance of that type, including all its fields (public and private). By contrast, variables or fields of reference types may hold null, or may refer to an object, stored elsewhere, to which any number of other references may also exist. The fields of a struct will be stored in the same place as the variable or field of that structure type, which may be either on the stack or may be part of another heap object.

Creating a variable or field of a primitive value type will create it with a default value; creating a variable or field of a structure type will create a new instance, creating all fields therein in the default manner. Creating a new instance of a reference type will start by creating all fields therein in the default manner, and then running optional additional code depending upon the type.

Copying one variable or field of a primitive type to another will copy the value. Copying one variable or field of structure type to another will copy all the fields (public and private) of the former instance to the latter instance. Copying one variable or field of reference type to another will cause the latter to refer to the same instance as the former (if any).

It's important to note that in some languages like C++, the semantic behavior of a type is independent of how it is stored, but that isn't true of .NET. If a type implements mutable value semantics, copying one variable of that type to another copies the properties of the first to another instance, referred to by the second, and using a member of the second to mutate it will cause that second instance to be changed, but not the first. If a type implements mutable reference semantics, copying one variable to another and using a member of the second to mutate the object will affect the object referred to by the first variable; types with immutable semantics do not allow mutation, so it doesn't matter semantically whether copying creates a new instance or creates another reference to the first.

In .NET, it is possible for value types to implement any of the above semantics, provided that all of their fields can do likewise. A reference type, however, can only implement mutable reference semantics or immutable semantics; value types with fields of mutable reference types are limited to either implementing mutable reference semantics or weird hybrid semantics.