Author: Jon Jagger
-
Is this shampoo?
Mini-bottles in hotel bathrooms… Am I the only one who can’t read the writing on them? I mean the font size is tiny. You can’t read them. There’s just no way to tell which bottles are which. Shampoo? Conditioner? Body lotion? Moisturiser? Shower gel?…
-
Is this shampoo?
Mini-bottles in hotel bathrooms… Am I the only one who can’t read the writing on them? I mean the font size is tiny. You can’t read them. There’s just no way to tell which bottles are which. Shampoo? Conditioner? Body lotion? Moisturiser? Shower gel?…
-
Reality is not what is seems
is an excellent book by Carlo Rovelli (isbn 978-0-141-98321-9) As usual I’m going to quote from a few pages. It doesn’t describe where there is a particle but how the particle shows itself to others. It isn’t things that enter into relations…
-
Reality is not what is seems
is an excellent book by Carlo Rovelli (isbn 978-0-141-98321-9) As usual I’m going to quote from a few pages. It doesn’t describe where there is a particle but how the particle shows itself to others. It isn’t things that enter into relations…
-
The Ascent of Man
is an excellent book by Jacob Bronowski (isbn 0-7088-2035-2) As usual I’m going to quote from a few pages. Evolution is the climbing of a ladder from the simple to the complex by steps, each of which is stable in itself. The turning point to…
-
The Ascent of Man
is an excellent book by Jacob Bronowski (isbn 0-7088-2035-2) As usual I’m going to quote from a few pages. Evolution is the climbing of a ladder from the simple to the complex by steps, each of which is stable in itself. The turning point to…
-
TDD – Romanes Eunt Domus!
-
TDD – Romanes Eunt Domus!
-
What is Life?
is an excellent book by Paul Nurse (isbn 978-1-788451-40-6) As usual I’m going to quote from a few pages. Cells repair these mutations, but they are not completely successful. If they were, all individuals of a species would be identical and evo…
-
What is Life?
is an excellent book by Paul Nurse (isbn 978-1-788451-40-6) As usual I’m going to quote from a few pages. Cells repair these mutations, but they are not completely successful. If they were, all individuals of a species would be identical and evo…
-
Sapiens. A Brief History of Human Kind
is an excellent book by Yuval Noah Harari (isbn 978-0-099-59008-8) As usual I’m going to quote from a few pages. Whereas chimpanzees spend five hours a day chewing raw food, a single hour suffices for people eating cooked food. Since long inte…
-
Sapiens. A Brief History of Human Kind
is an excellent book by Yuval Noah Harari (isbn 978-0-099-59008-8) As usual I’m going to quote from a few pages. Whereas chimpanzees spend five hours a day chewing raw food, a single hour suffices for people eating cooked food. Since long inte…
-
The Culture Code
is an excellent book by Daniel Coyle (isbn 978-1-847-94127-5) As usual I’m going to quote from a few pages. Much of the connection happens around the dinner table, as Popovich is obsessed with food and wine. One misconception about highly succ…
-
The Culture Code
is an excellent book by Daniel Coyle (isbn 978-1-847-94127-5) As usual I’m going to quote from a few pages. Much of the connection happens around the dinner table, as Popovich is obsessed with food and wine. One misconception about highly succ…
-
Life, the Universe and Everything
is an excellent book by Douglas Adams (isbn 978-0-330-49120-4). As usual I’m going to quote from a few pages. He would insult the universe. That is, he would insult everybody in it. Individually, personally, one by one, and (this was the thing t…
-
Life, the Universe and Everything
is an excellent book by Douglas Adams (isbn 978-0-330-49120-4). As usual I’m going to quote from a few pages. He would insult the universe. That is, he would insult everybody in it. Individually, personally, one by one, and (this was the thing t…
-
The Restaurant at the End of the Universe
is an excellent book by Douglas Adams (isbn 978-0-330-49121-1). As usual I’m going to quote from a few pages. There is another theory which states that this has already happened. The story so far: In the beginning the Universe was created. Thi…
-
The Restaurant at the End of the Universe
is an excellent book by Douglas Adams (isbn 978-0-330-49121-1). As usual I’m going to quote from a few pages. There is another theory which states that this has already happened. The story so far: In the beginning the Universe was created. Thi…
-
The Hitch Hiker’s Guide to the Galaxy
is an excellent book by Douglas Adams (isbn 978-0-330-49119-8). As usual I’m going to quote from a few pages. Far out in the uncharted backwaters of the unfashionable end of the Western Spiral Arm of the Galaxy lies a small unregarded yellow sun…
-
The Hitch Hiker’s Guide to the Galaxy
is an excellent book by Douglas Adams (isbn 978-0-330-49119-8). As usual I’m going to quote from a few pages. Far out in the uncharted backwaters of the unfashionable end of the Western Spiral Arm of the Galaxy lies a small unregarded yellow sun…
-
How to read water
is an excellent book by Tristan Gooley (isbn 978-1-473-61522-9). As usual I’m going to quote from a few pages: One of the universal truths of human observation is that we see more of what we expect to see and less of what we don’t expect to see…
-
How to read water
is an excellent book by Tristan Gooley (isbn 978-1-473-61522-9). As usual I’m going to quote from a few pages: One of the universal truths of human observation is that we see more of what we expect to see and less of what we don’t expect to see…
-
Organize for Complexity
is an excellent book by Niels Pflaeging (isbn 978-0-9915376-0-0). As usual I’m going to quote from a few pages: What Taylor pioneered was the idea of consistently dividing an organization between thinking people (managers) and executing people (w…
-
Organize for Complexity
is an excellent book by Niels Pflaeging (isbn 978-0-9915376-0-0). As usual I’m going to quote from a few pages: What Taylor pioneered was the idea of consistently dividing an organization between thinking people (managers) and executing people (w…
-
The book of five rings
is an excellent book by Miyamoto Musashi, translated by Thomas Cleary (isbn 1-57062-748-7). As usual I’m going to quote from a few pages: Preface: In common parlance, to do something with a real sword means to do it with utmost earnestness… The Bo…
-
The book of five rings
is an excellent book by Miyamoto Musashi, translated by Thomas Cleary (isbn 1-57062-748-7). As usual I’m going to quote from a few pages: Preface: In common parlance, to do something with a real sword means to do it with utmost earnestness… The Bo…
-
practising jQuery in cyber-dojo
cyber-dojo now supports practising jQuery. Enjoy 🙂
-
practising jQuery in cyber-dojo
cyber-dojo now supports practising jQuery. Enjoy 🙂
-
compile time assertions in C
C has a facility for checking dynamic assertions at run-time. It’s inside <assert.h> and its called assert. Now assert is a macro, so why isn’t it called ASSERT? I don’t know. Prior art no doubt. Anyway, assert is a dynamic runtime feature, you can only use it inside functions.
/* not inside a function, won't compile :-( */ assert(sizeof(int) * CHAR_BIT >= 32);
That’s a pity because it would be nice if I could get the compiler to check things like this automatically at compile time. I’ve occasionally seen an attempt at a compile-time check like this…
#if sizeof(int) * CHAR_BIT < 32 #error People of Earth. Your attention please... #endif
But this doesn’t work. The C preprocessor is a glorified text reformatter: it knows practically nothing about C. However, there is a way to write this as a compile time assertion (and moving any error trap to an earlier phase is a Good Thing)
{ yourself }
#define COMPILE_TIME_ASSERT(expr) \ char constraint[expr] COMPILE_TIME_ASSERT(sizeof(int) * CHAR_BIT >= 32);
What’s is going on here? Well, the example preprocesses to…
char constraint[sizeof(int) * CHAR_BIT >= 32];
If the expression is true, (an int is at least 32 bits), the expression will have a value of one, and constraint will be an array of one char. If the assertion is false, (an int is less than 32 bits), the expression will have a value of zero, and constraint will be an empty array. That’s illegal, and you’ll get a compile time error. Viola, a compile time assertion 🙂 You can use it inside and outside a function but you can’t use it twice in the same function, as you end up with a duplicate definition. To solve that problem you could resort to some convoluted macro trickery:
#define COMPILE_TIME_ASSERT(expr) char UNIQUE_NAME[expr] #define UNIQUE_NAME MAKE_NAME(__LINE__) #define MAKE_NAME(line) MAKE_NAME2(line) #define MAKE_NAME2(line) constraint_ ## line
But this is pretty horrible. Also, you will probably get warnings about unused variables. Take a step back for a moment and think about why it works at all. It’s because you have to specify the size of an array as a compile time constant. The formal grammar of a direct-declarator tells you this. Let’s look at some bits of grammar more closely:
Constrained arrays
direct-declarator: identifier ( declarator ) direct-declarator [ constant-expression opt ] direct-declarator ( parameter-type-list ) direct-declarator ( identifier-list opt )
I just piggy backed on this, using the constraint that the value of the constant expression cannot (in this context) be zero. A natural question (to the curious) is are there other parts of the formal grammar that require a constant expression. The answer, of course, is yes.
Constrained enums
enumerator: enumeration-constant enumeration-constant = constant-expression
However, I can’t use this because there are no useful constraints in this context.
Constrained bit-fields
struct-declarator: declarator declarator opt : constant-expression
Reading the constraints of a bit field I see that if the width of a bit-field is zero the declaration cannot have a declarator. In other words this is legal…
struct x { unsigned int : 0; };
but this is not…
struct x { unsigned int bf : 0; };
This suggests another way to create a compile time assertion
#define COMPILE_TIME_ASSERT(expr) \ struct x { unsigned int bf : expr; } COMPILE_TIME_ASSERT(sizeof(int) * CHAR_BIT >= 32);
Trying this we again get duplicate definitions, not of a variable this time, but of the type struct x. However we can fix this by creating an anonymous struct:
#define COMPILE_TIME_ASSERT(expr) \ struct { unsigned int bf : expr; }
This works. However, now you’ll probably get warnings about the unused untagged struct.
There is one last bit of grammar that uses a constant-expression.Constrained switch
labelled-statement: identifier : statement case constant-expression : statement default : statement
It’s well known that you can’t have two case labels with the same constant. The following will not compile…
switch (0) { case 0: case 0:; }
So, here’s yet another way to create a compile time assertion. This time we don’t create a dummy variable, or a dummy type, but a dummy statement. A dummy switch statement:#define COMPILE_TIME_ASSERT(pred) \ switch(0){case 0:case pred:;} COMPILE_TIME_ASSERT(sizeof(int) * CHAR_BIT >= 32);
If pred evaluates to true (i.e., 1) then the case labels will be 0 and 1. Different; Ok. If pred evaluates to false (i.e., 0) then the case labels will be 0 and 0. The same; Compile time error. Viola. However, a switch statement cannot exist in the global scope. So the last piece of the puzzle is to put the compile time assertions inside a function.
#include <limits.h> #define COMPILE_TIME_ASSERT(pred) \ switch(0){case 0:case pred:;} #define ASSERT_MIN_BITSIZE(type, size) \ COMPILE_TIME_ASSERT(sizeof(type) * CHAR_BIT >= size) #define ASSERT_EXACT_BITSIZE(type, size) \ COMPILE_TIME_ASSERT(sizeof(type) * CHAR_BIT == size) void compile_time_assertions(void) { ASSERT_MIN_BITSIZE(char, 8); ASSERT_MIN_BITSIZE(int, 16); ASSERT_EXACT_BITSIZE(long, 32); }
-
compile time assertions in C
C has a facility for checking dynamic assertions at run-time. It’s inside <assert.h> and its called assert. Now assert is a macro, so why isn’t it called ASSERT? I don’t know. Prior art no doubt. Anyway, assert is a dynamic runtime feature, you can only use it inside functions.
/* not inside a function, won't compile :-( */ assert(sizeof(int) * CHAR_BIT >= 32);
That’s a pity because it would be nice if I could get the compiler to check things like this automatically at compile time. I’ve occasionally seen an attempt at a compile-time check like this…
#if sizeof(int) * CHAR_BIT < 32 #error People of Earth. Your attention please... #endif
But this doesn’t work. The C preprocessor is a glorified text reformatter: it knows practically nothing about C. However, there is a way to write this as a compile time assertion (and moving any error trap to an earlier phase is a Good Thing)
{ yourself }
#define COMPILE_TIME_ASSERT(expr) \ char constraint[expr] COMPILE_TIME_ASSERT(sizeof(int) * CHAR_BIT >= 32);
What’s is going on here? Well, the example preprocesses to…
char constraint[sizeof(int) * CHAR_BIT >= 32];
If the expression is true, (an int is at least 32 bits), the expression will have a value of one, and constraint will be an array of one char. If the assertion is false, (an int is less than 32 bits), the expression will have a value of zero, and constraint will be an empty array. That’s illegal, and you’ll get a compile time error. Viola, a compile time assertion 🙂 You can use it inside and outside a function but you can’t use it twice in the same function, as you end up with a duplicate definition. To solve that problem you could resort to some convoluted macro trickery:
#define COMPILE_TIME_ASSERT(expr) char UNIQUE_NAME[expr] #define UNIQUE_NAME MAKE_NAME(__LINE__) #define MAKE_NAME(line) MAKE_NAME2(line) #define MAKE_NAME2(line) constraint_ ## line
But this is pretty horrible. Also, you will probably get warnings about unused variables. Take a step back for a moment and think about why it works at all. It’s because you have to specify the size of an array as a compile time constant. The formal grammar of a direct-declarator tells you this. Let’s look at some bits of grammar more closely:
Constrained arrays
direct-declarator: identifier ( declarator ) direct-declarator [ constant-expression opt ] direct-declarator ( parameter-type-list ) direct-declarator ( identifier-list opt )
I just piggy backed on this, using the constraint that the value of the constant expression cannot (in this context) be zero. A natural question (to the curious) is are there other parts of the formal grammar that require a constant expression. The answer, of course, is yes.
Constrained enums
enumerator: enumeration-constant enumeration-constant = constant-expression
However, I can’t use this because there are no useful constraints in this context.
Constrained bit-fields
struct-declarator: declarator declarator opt : constant-expression
Reading the constraints of a bit field I see that if the width of a bit-field is zero the declaration cannot have a declarator. In other words this is legal…
struct x { unsigned int : 0; };
but this is not…
struct x { unsigned int bf : 0; };
This suggests another way to create a compile time assertion
#define COMPILE_TIME_ASSERT(expr) \ struct x { unsigned int bf : expr; } COMPILE_TIME_ASSERT(sizeof(int) * CHAR_BIT >= 32);
Trying this we again get duplicate definitions, not of a variable this time, but of the type struct x. However we can fix this by creating an anonymous struct:
#define COMPILE_TIME_ASSERT(expr) \ struct { unsigned int bf : expr; }
This works. However, now you’ll probably get warnings about the unused untagged struct.
There is one last bit of grammar that uses a constant-expression.Constrained switch
labelled-statement: identifier : statement case constant-expression : statement default : statement
It’s well known that you can’t have two case labels with the same constant. The following will not compile…
switch (0) { case 0: case 0:; }
So, here’s yet another way to create a compile time assertion. This time we don’t create a dummy variable, or a dummy type, but a dummy statement. A dummy switch statement:#define COMPILE_TIME_ASSERT(pred) \ switch(0){case 0:case pred:;} COMPILE_TIME_ASSERT(sizeof(int) * CHAR_BIT >= 32);
If pred evaluates to true (i.e., 1) then the case labels will be 0 and 1. Different; Ok. If pred evaluates to false (i.e., 0) then the case labels will be 0 and 0. The same; Compile time error. Viola. However, a switch statement cannot exist in the global scope. So the last piece of the puzzle is to put the compile time assertions inside a function.
#include <limits.h> #define COMPILE_TIME_ASSERT(pred) \ switch(0){case 0:case pred:;} #define ASSERT_MIN_BITSIZE(type, size) \ COMPILE_TIME_ASSERT(sizeof(type) * CHAR_BIT >= size) #define ASSERT_EXACT_BITSIZE(type, size) \ COMPILE_TIME_ASSERT(sizeof(type) * CHAR_BIT == size) void compile_time_assertions(void) { ASSERT_MIN_BITSIZE(char, 8); ASSERT_MIN_BITSIZE(int, 16); ASSERT_EXACT_BITSIZE(long, 32); }
-
C# struct/class differences
- Events are locked?
- Exist on stack or heap?
- Can cause garbage collection?
- Meaning of this?
- Always has a default constructor?
- Default construction triggers static construction?
- Can be null?
- Use with the as operator?
- Can be locked?
- Can have a destructor?
- Default field layout?
- Can be a volatile field?
- Can have synchronized methods?
- Can be pointed to?
- Can be stackalloc’d?
- Can be sizeof’d?
- How to initialize fields?
- Inheritance differences?
- Equals behavior
Events are locked?
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.
Exist on stack or heap?
Value type local instances are allocated on the stack. Reference type local instances are allocated on the heap.
Can cause garbage collection?
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.
Meaning of this?
In a class, this is classified as a value, and thus cannot appear on the left hand side of an assignment, or be used as a ref/out parameter. For example:
class Indirect { //... public void Method(Indirect that) { RefParameter(ref this); // compile-time error OutParameter(out this); // compile-time error this = that; // compile-time error } //... }
In a struct, this is classified as an out parameter in a constructor and as a ref parameter in all other function members. Thus it is possible to modify the entire structure by assigning to this or passing this as a ref/out parameter. For example:struct Direct { //... public void Reassign(Direct that) { RefParameter(ref this); // compiles ok OutParameter(out this); // compiles ok this = that; // compiles ok } //... }
Furthermore, you can reassign a whole struct even when the struct contains readonly fields!struct Direct { public Direct(int value) { Field = value; } public void Reassign(Direct that) { RefParameter(ref this); // compiles ok OutParameter(out this); // compiles ok this = that; // compiles ok } public readonly int Field; } class Show { static void Main() { Direct s = new Direct(42); Console.WriteLine(s.Field); // writes 42 s.Reassign(new Direct(24)); Console.WriteLine(s.Field); // writes 24 } }
Note however that when you call a method on a readonly value-type field, the method call is made on a copy of the field.struct Direct { // as above } class Caller { public void Method() { Console.WriteLine(d.Field); // writes 42 d.Reassign(new Direct(24)); Console.WriteLine(d.Field); // writes 42! } private readonly Direct d = new Direct(42); } class Show { static void Main() { Caller c = new Caller(); c.Method(); } }
Always have a default constructor?
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 c'tor 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 { } }
Default construction triggers static constructor?
A structs static constructor is not triggered by calling the structs default constructor. It is for a class.
struct Direct { static Direct() { Console.WriteLine("This is not written"); } } class NotTriggered { static void Main() { Direct local = new Direct(); } }
Can be null?
A struct instance cannot be null.
class Nullness { static void Eg(Direct s, Indirect c) { if (s == null) ... // compile-time error if (c == null) ... // compiles ok } }
Use with the as operator?
A struct type cannot be the right hand side operand of the as operator.
class Fragment { static void Eg(Direct s, Indirect c) { Direct no = s as Direct; // compile-time error InDirect yes = c as InDirect; // compiles ok //... } }
Can be locked?
A struct type expression cannot be the operand of a lock statement.
class LockStatement { static void Eg(Direct s, InDirect c) { lock(s) { ... } // compile-time error lock(c) { ... } // compiles ok } }
Can have a destructor?
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
Default field layout?
The default [StructLayout] attribute (which lives in the System.Runtime.InteropServices namespace) for a struct is LayoutKind.Sequential whereas the default StructLayout for a class is LayoutKind.Auto. (And yes, despite its name you can tag a class with the StructLayout attribute.) In other words the CIL for this:
public struct Direct { //... }
looks like this:
.class public sequential ansi sealed beforefieldinit Direct extends [mscorlib]System.ValueType { //... }
whereas the CIL for this:
public sealed class InDirect { //... }
looks like this:
.class public auto ansi sealed beforefieldinit Indirect extends [mscorlib]System.Object { //... }
Can be a volatile field?
You can’t declare a user-defined struct type as a volatile field but you can declare a user-defined class type as a volatile field.
class Bad { private volatile Direct field; // compile-time error } class Good { private volatile Indirect field; // compiles ok }
Can have synchronized methods?
You can’t use the [MethodImpl(MethodImplOptions.Synchronized)] attribute on methods of a struct type (if you call the method you get a runtime TypeLoadException) whereas you can use the [MethodImpl(MethodImplOptions.Synchronized)] attribute on methods of a class type.
using System.Runtime.CompilerServices; class Indirect { [MethodImpl(MethodImplOptions.Synchronized)] // compiles and runs ok public void Method() { //... } } struct Direct { [MethodImpl(MethodImplOptions.Synchronized)] // compiles ok, runtime TypeLoadException public void Method() { //... } }
Can be pointed to?
Clause 25.2 of the C# standard defines an unmanaged type as any type that isn’t a reference type and doesn’t contain reference-type fields at any level of nesting. That is, one of the following:
- Any simple value type (11.1.3, eg byte, int, long, double, bool, etc).
- Any enum type.
- Any pointer type.
- Any user-defined struct-type that contains fields of unmanaged types only.
class Bad { static void Main() { Indirect variable = new Indirect(); unsafe { fixed(Indirect * ptr = &variable) // compile-time error { //... } } } }
If you want to fix an unmanaged instance you have to do so by fixing it through an unmanaged field. For example:class Indirect { public int fixHandle; } class Bad { static void Main() { Indirect variable = new Indirect(); unsafe { fixed(int * ptr = &variable.fixHandle) // compiles ok { //... } } } }
In contrast, you can (nearly) always take the address of an unmanaged instance.struct Direct { // no reference fields at any level of nesting } class SimpleCase { static void Main() { Direct variable = new Direct(); unsafe { Direct * ptr = &variable; // compiles ok //... } } }
However, you have to take the address inside a fixed statement if the variable is moveable (subject to relocation by the garbage collector, see 25.3 and example above). Also, you can never take the address of a volatile field.So, in summary, you can never create a pointer to a class type but you sometimes create a pointer to a struct type.
Can be stackalloc’d?
You can only use stackalloc on unmanaged types. Hence you can never use stackalloc on class types. For example:
class Indirect { //... } class Bad { static void Main() { unsafe { Indirect * array = stackalloc Indirect[42]; // compile-time error //... } } }
Where as you can use stackalloc on struct types that are unmanaged. For example:struct Direct { // no reference fields at any level of nesting } class Good { static void Main() { unsafe { Direct * array = stackalloc Direct[42]; // compiles ok //... } } }
Can be sizeof’d?
You can only use sizeof on unmanaged types. Hence you can never use sizeof on class types. For example:
class Indirect { //... } class Bad { static void Main() { unsafe { int size = sizeof(Indirect); // compile-time error //... } } }
Where as you can use sizeof on struct types that are unmanaged. For example:struct Direct { // no reference fields at any level of nesting } class Good { static void Main() { unsafe { int size = sizeof(Direct); // compiles ok //... } } }
How to initialize fields?
The fields of a class have a default initialization to zero/false/null. The fields of a struct have no default value.
struct Direct { public int Field; } class Indirect { public Indirect() { } //... public int Field; } class Defaults { static void Main() { Direct s; Console.WriteLine(s.Field); // compile-time error Indirect c = new Indirect(); Console.WriteLine(c.Field); // compiles ok } }
You can initialize fields in a class at their point of declaration. For example:
class Indirect { //... private int field = 42; }
You can’t do this for fields in a struct. For example:struct Direct { //... private int field = 42; // compile-time error }
Fields in a struct have to be initialized in a constructor. For example:struct Direct { public Direct(int value) { field = value; } //... private int field; // compiles ok }
Also, the definite assignment rules of a struct are tracked on an individual field basis. This means you can bypass initialization and “assign” the fields of a struct one a time. For example:struct Direct { public int X, Y; } class Example { static void Main() { Direct d; d.X = 42; Console.WriteLine(d.X); // compiles ok Console.WriteLine(d.Y); // compile-time error } }
Inheritance differences?
- 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 (eg 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().
Equals behavior?
classes inherit Object.Equals which implements identity equality whereas structs inherit ValueType.Equals which implements value equality.
using System.Diagnostics; struct Direct { public Direct(int value) { field = value; } private int field; } class Indirect { public Indirect(int value) { field = value; } private int field; } class EqualsBehavior { static void Main() { Direct s1 = new Direct(42); Direct s2 = new Direct(42); Indirect c1 = new Indirect(42); Indirect c2 = new Indirect(42); bool structEquality = s1.Equals(s2); bool classIdentity = !c1.Equals(c2); Debug.Assert(structEquality); Debug.Assert(classIdentity); } }
Overriding Equals for structs should be faster because it can avoid reflection and boxing.struct Direct { public Direct(int value) { field = value; } public override bool Equals(object other) { return other is Direct && Equals((Direct)other); } public static bool operator==(Direct lhs, Direct rhs) { return lhs.Equals(rhs); } //... private bool Equals(Direct other) { return field == other.field; } private int field; }
-
C# struct/class differences
- Events are locked?
- Exist on stack or heap?
- Can cause garbage collection?
- Meaning of this?
- Always has a default constructor?
- Default construction triggers static construction?
- Can be null?
- Use with the as operator?
- Can be locked?
- Can have a destructor?
- Default field layout?
- Can be a volatile field?
- Can have synchronized methods?
- Can be pointed to?
- Can be stackalloc’d?
- Can be sizeof’d?
- How to initialize fields?
- Inheritance differences?
- Equals behavior
Events are locked?
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.
Exist on stack or heap?
Value type local instances are allocated on the stack. Reference type local instances are allocated on the heap.
Can cause garbage collection?
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.
Meaning of this?
In a class, this is classified as a value, and thus cannot appear on the left hand side of an assignment, or be used as a ref/out parameter. For example:
class Indirect { //... public void Method(Indirect that) { RefParameter(ref this); // compile-time error OutParameter(out this); // compile-time error this = that; // compile-time error } //... }
In a struct, this is classified as an out parameter in a constructor and as a ref parameter in all other function members. Thus it is possible to modify the entire structure by assigning to this or passing this as a ref/out parameter. For example:struct Direct { //... public void Reassign(Direct that) { RefParameter(ref this); // compiles ok OutParameter(out this); // compiles ok this = that; // compiles ok } //... }
Furthermore, you can reassign a whole struct even when the struct contains readonly fields!struct Direct { public Direct(int value) { Field = value; } public void Reassign(Direct that) { RefParameter(ref this); // compiles ok OutParameter(out this); // compiles ok this = that; // compiles ok } public readonly int Field; } class Show { static void Main() { Direct s = new Direct(42); Console.WriteLine(s.Field); // writes 42 s.Reassign(new Direct(24)); Console.WriteLine(s.Field); // writes 24 } }
Note however that when you call a method on a readonly value-type field, the method call is made on a copy of the field.struct Direct { // as above } class Caller { public void Method() { Console.WriteLine(d.Field); // writes 42 d.Reassign(new Direct(24)); Console.WriteLine(d.Field); // writes 42! } private readonly Direct d = new Direct(42); } class Show { static void Main() { Caller c = new Caller(); c.Method(); } }
Always have a default constructor?
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 c'tor 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 { } }
Default construction triggers static constructor?
A structs static constructor is not triggered by calling the structs default constructor. It is for a class.
struct Direct { static Direct() { Console.WriteLine("This is not written"); } } class NotTriggered { static void Main() { Direct local = new Direct(); } }
Can be null?
A struct instance cannot be null.
class Nullness { static void Eg(Direct s, Indirect c) { if (s == null) ... // compile-time error if (c == null) ... // compiles ok } }
Use with the as operator?
A struct type cannot be the right hand side operand of the as operator.
class Fragment { static void Eg(Direct s, Indirect c) { Direct no = s as Direct; // compile-time error InDirect yes = c as InDirect; // compiles ok //... } }
Can be locked?
A struct type expression cannot be the operand of a lock statement.
class LockStatement { static void Eg(Direct s, InDirect c) { lock(s) { ... } // compile-time error lock(c) { ... } // compiles ok } }
Can have a destructor?
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
Default field layout?
The default [StructLayout] attribute (which lives in the System.Runtime.InteropServices namespace) for a struct is LayoutKind.Sequential whereas the default StructLayout for a class is LayoutKind.Auto. (And yes, despite its name you can tag a class with the StructLayout attribute.) In other words the CIL for this:
public struct Direct { //... }
looks like this:
.class public sequential ansi sealed beforefieldinit Direct extends [mscorlib]System.ValueType { //... }
whereas the CIL for this:
public sealed class InDirect { //... }
looks like this:
.class public auto ansi sealed beforefieldinit Indirect extends [mscorlib]System.Object { //... }
Can be a volatile field?
You can’t declare a user-defined struct type as a volatile field but you can declare a user-defined class type as a volatile field.
class Bad { private volatile Direct field; // compile-time error } class Good { private volatile Indirect field; // compiles ok }
Can have synchronized methods?
You can’t use the [MethodImpl(MethodImplOptions.Synchronized)] attribute on methods of a struct type (if you call the method you get a runtime TypeLoadException) whereas you can use the [MethodImpl(MethodImplOptions.Synchronized)] attribute on methods of a class type.
using System.Runtime.CompilerServices; class Indirect { [MethodImpl(MethodImplOptions.Synchronized)] // compiles and runs ok public void Method() { //... } } struct Direct { [MethodImpl(MethodImplOptions.Synchronized)] // compiles ok, runtime TypeLoadException public void Method() { //... } }
Can be pointed to?
Clause 25.2 of the C# standard defines an unmanaged type as any type that isn’t a reference type and doesn’t contain reference-type fields at any level of nesting. That is, one of the following:
- Any simple value type (11.1.3, eg byte, int, long, double, bool, etc).
- Any enum type.
- Any pointer type.
- Any user-defined struct-type that contains fields of unmanaged types only.
class Bad { static void Main() { Indirect variable = new Indirect(); unsafe { fixed(Indirect * ptr = &variable) // compile-time error { //... } } } }
If you want to fix an unmanaged instance you have to do so by fixing it through an unmanaged field. For example:class Indirect { public int fixHandle; } class Bad { static void Main() { Indirect variable = new Indirect(); unsafe { fixed(int * ptr = &variable.fixHandle) // compiles ok { //... } } } }
In contrast, you can (nearly) always take the address of an unmanaged instance.struct Direct { // no reference fields at any level of nesting } class SimpleCase { static void Main() { Direct variable = new Direct(); unsafe { Direct * ptr = &variable; // compiles ok //... } } }
However, you have to take the address inside a fixed statement if the variable is moveable (subject to relocation by the garbage collector, see 25.3 and example above). Also, you can never take the address of a volatile field.So, in summary, you can never create a pointer to a class type but you sometimes create a pointer to a struct type.
Can be stackalloc’d?
You can only use stackalloc on unmanaged types. Hence you can never use stackalloc on class types. For example:
class Indirect { //... } class Bad { static void Main() { unsafe { Indirect * array = stackalloc Indirect[42]; // compile-time error //... } } }
Where as you can use stackalloc on struct types that are unmanaged. For example:struct Direct { // no reference fields at any level of nesting } class Good { static void Main() { unsafe { Direct * array = stackalloc Direct[42]; // compiles ok //... } } }
Can be sizeof’d?
You can only use sizeof on unmanaged types. Hence you can never use sizeof on class types. For example:
class Indirect { //... } class Bad { static void Main() { unsafe { int size = sizeof(Indirect); // compile-time error //... } } }
Where as you can use sizeof on struct types that are unmanaged. For example:struct Direct { // no reference fields at any level of nesting } class Good { static void Main() { unsafe { int size = sizeof(Direct); // compiles ok //... } } }
How to initialize fields?
The fields of a class have a default initialization to zero/false/null. The fields of a struct have no default value.
struct Direct { public int Field; } class Indirect { public Indirect() { } //... public int Field; } class Defaults { static void Main() { Direct s; Console.WriteLine(s.Field); // compile-time error Indirect c = new Indirect(); Console.WriteLine(c.Field); // compiles ok } }
You can initialize fields in a class at their point of declaration. For example:
class Indirect { //... private int field = 42; }
You can’t do this for fields in a struct. For example:struct Direct { //... private int field = 42; // compile-time error }
Fields in a struct have to be initialized in a constructor. For example:struct Direct { public Direct(int value) { field = value; } //... private int field; // compiles ok }
Also, the definite assignment rules of a struct are tracked on an individual field basis. This means you can bypass initialization and “assign” the fields of a struct one a time. For example:struct Direct { public int X, Y; } class Example { static void Main() { Direct d; d.X = 42; Console.WriteLine(d.X); // compiles ok Console.WriteLine(d.Y); // compile-time error } }
Inheritance differences?
- 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 (eg 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().
Equals behavior?
classes inherit Object.Equals which implements identity equality whereas structs inherit ValueType.Equals which implements value equality.
using System.Diagnostics; struct Direct { public Direct(int value) { field = value; } private int field; } class Indirect { public Indirect(int value) { field = value; } private int field; } class EqualsBehavior { static void Main() { Direct s1 = new Direct(42); Direct s2 = new Direct(42); Indirect c1 = new Indirect(42); Indirect c2 = new Indirect(42); bool structEquality = s1.Equals(s2); bool classIdentity = !c1.Equals(c2); Debug.Assert(structEquality); Debug.Assert(classIdentity); } }
Overriding Equals for structs should be faster because it can avoid reflection and boxing.struct Direct { public Direct(int value) { field = value; } public override bool Equals(object other) { return other is Direct && Equals((Direct)other); } public static bool operator==(Direct lhs, Direct rhs) { return lhs.Equals(rhs); } //... private bool Equals(Direct other) { return field == other.field; } private int field; }
-
ACCU C++ Countdown Pub Quiz
The ACCU conference is one of the highlights of my year. I ran a brand new session, a C++ Pub Quiz with an emphasis on fun and interaction, based loosely on the popular UK TV game show Countdown. In the TV version, contestants play individually …
-
ACCU C++ Countdown Pub Quiz
The ACCU conference is one of the highlights of my year. I ran a brand new session, a C++ Pub Quiz with an emphasis on fun and interaction, based loosely on the popular UK TV game show Countdown. In the TV version, contestants play individually …
-
Docker in Action
is an excellent book by Jeff Nickoloff. As usual I’m going to quote from a few pages. The docker stop command tells the program with PID #1 in the container to halt. Like most Docker isolation features, you can optionally create containers …
-
Docker in Action
is an excellent book by Jeff Nickoloff. As usual I’m going to quote from a few pages. The docker stop command tells the program with PID #1 in the container to halt. Like most Docker isolation features, you can optionally create containers …
-
Siddhartha
is an excellent book by Herman Hesse. As usual I’m going to quote from a few pages. He learned more from the river than Vasudeva could teach him. He learned from it continually. Above all, he learned from it how to listen, to listen with a st…
-
Siddhartha
is an excellent book by Herman Hesse. As usual I’m going to quote from a few pages. He learned more from the river than Vasudeva could teach him. He learned from it continually. Above all, he learned from it how to listen, to listen with a st…
-
The Hidden Life of Trees
is an excellent book by Peter Wohlleben (isbn 1771642483). As usual I’m going to quote from a few pages. In forked trees, at a certain point, two main shoots form, they continue to grow alongside each other. Each side of the fork creates it…
-
The Hidden Life of Trees
is an excellent book by Peter Wohlleben (isbn 1771642483). As usual I’m going to quote from a few pages. In forked trees, at a certain point, two main shoots form, they continue to grow alongside each other. Each side of the fork creates it…
-
the DevOps Handbook
is an excellent book by Gene Kim, Jez Humble, Patrick Debois, and John Willis (isbn 978-1-942788-00-3). As usual I’m going to quote from a few pages. Make infrastructure easier to rebuild than to repair. The average age of a Netflix AWS ins…
-
the DevOps Handbook
is an excellent book by Gene Kim, Jez Humble, Patrick Debois, and John Willis (isbn 978-1-942788-00-3). As usual I’m going to quote from a few pages. Make infrastructure easier to rebuild than to repair. The average age of a Netflix AWS ins…
-
NDC Does C++ Countdown!
It was my pleasure to run a small workshop style session at the excellent NDC-London conference. I ran a fun C++ game which parodies the popular UK TV gameshow Countdown. In the TV version contestants take turns picking 9 random vowels/consonants…
-
NDC Does C++ Countdown!
It was my pleasure to run a small workshop style session at the excellent NDC-London conference. I ran a fun C++ game which parodies the popular UK TV gameshow Countdown. In the TV version contestants take turns picking 9 random vowels/consonants…
-
the design and implementation of cyber-dojo
At the excellent Agile on the Beach conference in Cornwall I did a presentation outlining some of the history, design and implementation of cyber-dojo. The video has just gone live on youtube.
-
the design and implementation of cyber-dojo
At the excellent Agile on the Beach conference in Cornwall I did a presentation outlining some of the history, design and implementation of cyber-dojo. The video has just gone live on youtube.
-
Continuous Delivery
Is an excellent book by Jez Humble and Dave Farley. As usual I’m going to quote from a few pages… Software delivers no value until it is in the hands of its users. The pattern that is central to this book is the deployment pipeline. It sho…
-
Continuous Delivery
Is an excellent book by Jez Humble and Dave Farley. As usual I’m going to quote from a few pages… Software delivers no value until it is in the hands of its users. The pattern that is central to this book is the deployment pipeline. It sho…
-
Building Microservices
Is an excellent book by Sam Newman. As usual I’m going to quote from a few pages… Because microservices are primarily modeled around business domains, they avoid the problems of traditional tiered architectures. Microservices should cleanl…
-
Building Microservices
Is an excellent book by Sam Newman. As usual I’m going to quote from a few pages… Because microservices are primarily modeled around business domains, they avoid the problems of traditional tiered architectures. Microservices should cleanl…