C#: What style of data containers are preferred in general?
Asked Answered
C

8

14

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?

Cropdusting answered 13/7, 2009 at 11:46 Comment(4)
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.)Deina
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... :pCropdusting
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...Cropdusting
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.Deina
T
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.

Tag answered 13/7, 2009 at 11:46 Comment(3)
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 :)Cropdusting
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...Cropdusting
@Cropdusting - 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.Tag
B
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.

Biyearly answered 13/7, 2009 at 11:46 Comment(0)
D
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.)

Deina answered 13/7, 2009 at 11:46 Comment(0)
C
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.

Courtyard answered 13/7, 2009 at 11:46 Comment(4)
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 =)Cropdusting
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.Courtyard
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 classCourtyard
Also: In your case Immutable object should not have an empty ctor. Mutable should.Courtyard
V
0

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

Voltameter answered 13/7, 2009 at 11:46 Comment(0)
L
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.

Lines answered 13/7, 2009 at 11:46 Comment(0)
G
0

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

Guff answered 13/7, 2009 at 11:46 Comment(2)
Hm, wouldn't that depend on the processor? Or is it true for all of them, past and present?Cropdusting
It would. Cache lines of all modern x86 processors used in desktops are at least 16 bytes wide and probably no wider than 32.Guff
H
0

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

Helmholtz answered 13/7, 2009 at 11:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.