13

When creating a simple data container class, what should it be?

  • Class or struct?
  • Mutable or immutable?
  • With or without non-empty constructor?

Examples of the above:

struct MutableStruct
{
    public string Text { get; set; }
    public int Number { get; set; }
}

struct ImmutableStruct
{
    public string Text { get; private set; }
    public int Number { get; private set; }
    public ImmutableStruct(string text, int number)
        : this()
    {
        Text = text;
        Number = number;
    }
}

struct MutableStructWithConstructor
{
    public string Text { get; set; }
    public int Number { get; set; }
    public MutableStructWithConstructor(string text, int number)
        : this()
    {
        Text = text;
        Number = number;
    }
}
class MutableClass
{
    public string Text { get; set; }
    public int Number { get; set; }
}

class ImmutableClass
{
    public string Text { get; private set; }
    public int Number { get; private set; }
    public ImmutableClass(string text, int number)
    {
        Text = text;
        Number = number;
    }
}

class MutableClassWithConstructor
{
    public string Text { get; set; }
    public int Number { get; set; }
    public MutableClassWithConstructor(string text, int number)
    {
        Text = text;
        Number = number;
    }
}

Any good reasons we should choose one above another? Or are there mostly subjective preferences that separate them? Or does it depend a lot on the spesific use cases? If so in what use cases should you choose what and why?

Svish
  • 138,188
  • 158
  • 423
  • 589
  • 1
    By the way, your `ImmutableStruct` and `ImmutableClass` aren't truly immutable: The properties have private setters but their state can be changed from within the struct/class itself. (This is a shortcut that I often use myself, but it would be better to avoid the auto-property and use a readonly member variable instead, exposed via a public getter.) – LukeH Jul 13 '09 at 13:56
  • Hm, that is actually a good point, although I think I might be too lazy to do anything about it :p Especially since a simple data container class shouldn't do anything with its data anyways. How it is on the inside matters less to me kind of... Although maybe a public readonly field would be better? Kind of like properties though... :p – Svish Jul 13 '09 at 14:02
  • Hmm... `public string Text { get; }` should create a readonly backing field and enable setting in constructor only, like with readonly fields. At the moment I think that line creates a useless property returning null... – Svish Jul 13 '09 at 14:22
  • 1
    Yep, you need to do something like this instead: `private readonly string _Text; public string Text { get { return _Text; } }`. It's a shame that you can't mark a property as `readonly`, maybe in some future version of C#, but I wouldn't bet on it. – LukeH Jul 13 '09 at 14:26

8 Answers8

11

Almost always a class; structs should really only be used for things that are values - for example, a complex-number, or a currency type/value pair - and should almost-without-exclusion be immutable.

A parameterless constructor is handy on mutable data if you are going to do data-binding, as this allows the system to create instances without additional code from yourself. A non-empty constructor is pretty-much essential for immutable data. For mutable data, an object initializer goes a long way towards that (although isn't quite the same in terms of validation etc):

var obj = new Person {Name="Fred", DateOfBirth=DateTime.Today};

Whether your types are immutable is up to you; mutable makes it easier to do data-binding and serialization. In general, you tend to see more mutable types in .NET, but this may change as we get into the parallel / many-core era.

Marc Gravell
  • 927,783
  • 236
  • 2,422
  • 2,784
  • Thanks for very good explanation of what structs should be used for! Never really gotten that before, but now it suddenly made a lot more sense :) – Svish Jul 13 '09 at 12:59
  • On the matter of "mutable makes it easier to do data-binding". I would say mutable would be even required for at least two-way data binding, right? Immutable would restrict you to one-way data-binding. I think? And if that is correct I would say it is a matter of which one you want, rather than making things easier... – Svish Jul 13 '09 at 13:03
  • @Svish - it depends on the specific scenario; there are ways of doing data-binding to immutable data (for example how the PropertyGrid int the VS IDE *sometimes* lets you edit deeply immutable data) - but it requires implementing arcane interfaces. – Marc Gravell Jul 13 '09 at 14:06
3
  • You should almost always prefer classes over structs. Use structs only when the object represents an (immutable) value.

  • If you need to change the object and it is safe to do so then make it mutable, else make it immutable and use cloning.

  • If the object is in a valid state when created with the default constructor, fine. Otherwise always provide your own constructor.

codymanix
  • 25,944
  • 18
  • 83
  • 142
2

Only use structs when you need value-type semantics, and try to avoid mutable structs completely.

Other than that, I think it comes down to personal, or team, preference and your particular requirements.

(These days I'm trying to use immutable objects whenever possible, which almost always necessitates passing values into the constructor.)

LukeH
  • 242,140
  • 52
  • 350
  • 400
1

A couple of points on classes vs structs:

Classes are pass-by-reference which means that the same instance object is passed between functions. If you create MyClass in method A and call method B, method B changes the class and returns nothing to method A, method A sees the changes made in MyClass because they are refering to the same object. A reference is usually an int32, so no matter how big your class is it will be quick to call method B because it's only passing 4 bytes. Classes rely on the Garbage Collector to decide when it is no longer being used (smaller overhead in passing the class around but adds overhead to the garbage collector).

Structs are pass-by-value (or a value type). This means that the whole struct is copied when it is passed around. If your struct is big, this will take a long time. A change to the struct in Method B won’t show in method A unless it is returned (again costing time as it is pass by value), and Method A reads the return value. Structs are created on the stack and do not have a garbage collection overhead (you can see this if you examine your IL code).

There are many limitations in structs compared to classes, such as the lack of virtual methods and other polymorphic functionality.

It's best to favour classes, unless you are going to rapidly create and discard lots of objects in the same method (which drain system resources due to garbage collection), in which case favour structs.

monibius
  • 1,545
  • 16
  • 27
  • This wasn't a question on what the difference between a class and a struct was though... It was which one should you use, why, and should it be mutable or immutable when it comes to simple method-less data structures =) – Svish Jul 13 '09 at 13:59
  • Sure. Immutable is a defensive way to program, there are less things to 'go wrong', e.g. you didnt expect someone to set Text to null and still fill out Number by making a particular method call. But... In your case there are no methods, and there never will be. So there is no point in using immutable for that reason. The next reason to use immutable is because the surrounding functions didn't expect the value contained within the object to change (in a certain way) - Immutable still makes sense for you here. – monibius Jul 13 '09 at 14:23
  • You wont make much use of polymorphism with no methods so... Order of preference in you case: 1: Immutable class 2: Immutable struct 3: Mutable class 4: Mutable struct Unless you suffer from the garbage collection performance problems discussed in my post, in which case: 1: Immutable struct 2: Mutable struct 3: Immutable class 4: Mutable class – monibius Jul 13 '09 at 14:25
  • Also: In your case Immutable object should not have an empty ctor. Mutable should. – monibius Jul 13 '09 at 14:30
0

I usually do immutable classes with all the values being set/passed in via the constructor.

Chuck Conway
  • 15,795
  • 10
  • 56
  • 99
0

Also, there's the rule of thumb that structs shouldn't be larger than 16 bytes - the size of a processor cache line.

CannibalSmith
  • 4,522
  • 9
  • 42
  • 52
  • Hm, wouldn't that depend on the processor? Or is it true for all of them, past and present? – Svish Jul 13 '09 at 12:15
  • It would. Cache lines of all modern x86 processors used in desktops are at least 16 bytes wide and probably no wider than 32. – CannibalSmith Jul 17 '09 at 11:14
0

Consider using classes whenever you need to provide object "functionality" as opposed to merely identity or state i.e. "values".

As has already been stated, structs are value types and as such will be copied wherever you use them. Also, there is a negligible performance difference when instantiating classes versus structs.

Mike J
  • 2,921
  • 18
  • 20
0

It really seems like almost all of the time structs are an unnecessary distraction from the ease and efficiency of classes.

Alex Baranosky
  • 44,548
  • 39
  • 95
  • 146