views:

377

answers:

8

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?

+10  A: 

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
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
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
@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
A: 

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

Chuck Conway
+3  A: 
  • 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
+2  A: 

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
A: 

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

CannibalSmith
Hm, wouldn't that depend on the processor? Or is it true for all of them, past and present?
Svish
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
+1  A: 

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.

Ben Breen
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
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.
Ben Breen
You wont make much use of polymorphism with no methods so...Order of preference in you case:1: Immutable class2: Immutable struct3: Mutable class4: Mutable structUnless you suffer from the garbage collection performance problems discussed in my post, in which case:1: Immutable struct2: Mutable struct3: Immutable class4: Mutable class
Ben Breen
Also: In your case Immutable object should not have an empty ctor. Mutable should.
Ben Breen
A: 

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
A: 

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

Alex Baranosky