开发者

.NET immutable object usage best practices? Should I be using them as much as possible?

开发者 https://www.devze.com 2022-12-25 21:09 出处:网络
Say I have a simple object such as class Something { public int SomeInt { get; set; } } I have read that usin开发者_JS百科g immutable objects are faster and a better means of using business objects

Say I have a simple object such as

class Something
{
   public int SomeInt { get; set; }
}

I have read that usin开发者_JS百科g immutable objects are faster and a better means of using business objects? If this is so, should i strive to make all my objects as such:

class ImmutableSomething
{
   public int SomeInt { get { return m_someInt; } }
   private int m_someInt = 0;

   public void ChangeSomeInt(int newValue)
   {
       m_someInt = newvalue;
   }
}

What do you reckon?


What you depict is not an immutable object; simply moving the set code into a dedicated setter method doesn't make an object immutable. An immutable object, by definition, can't change, so the fact that you can alter the value of any of the object's properties or fields means that it isn't immutable.

In terms of "faster" or "better", immutable objects are not intrinsically faster or "better" than mutable objects; there isn't anything special about them, other than the fact that you can't change any values.


As others have said what you've posted isn't immutable. This is what an immutable object looks like. The readonly keyword means that the only place that the backing field for the property can be set is in the constructor. Essentially, after the object is constructed that's it forever.

public class ImmutableSomething
{
    private readonly int _someInt;
    public int SomeInt
    {
        get
        {
            return _someInt;
        }
    }

    public ImmutableSomething(int i)
    {
        _someInt = i;
    }

    public ImmutableSomething Add(int i){
        return new ImmutableSomething(_someInt + i);
    }
}

This is a big deal in functional programming because instead of talking about objects you get to talk about Values. Values never change, so you know that when you pass them to a function or method your original value will never be changed or updated. With mutable objects you can't make that guarantee.

Code built with immutable objects can be easily parallelized because there is no writable shared state. If some other thread gets your Value and wants to do something to it, it can't. "Changing" it in any way produces a new object with a brand new spot in memory just for that object.

So once you're dealing with values you can do some special things like interning which is what .NET does with strings. Because "Hello World" is a value and will never change, one reference to "Hello World" is just as good as any other, so instead of having "Hello World" in a hundred slots in memory, you have it in one slot and set all the references to "Hello World" to point to that one slot. So you get a big win on the memory side but you pay a performance penalty because whenever you create a new string you have to check the intern pool to make sure it doesn't already exist.


The primary advantage of deeply immutable objects is that it's very easy to take a "snapshot" of their properties--simply copy a reference to the object. No matter how big or complicated the object might be, one can "snapshot" the whole thing simply by copying one reference.

By contrast, if one wants to take a snapshot of a mutable object's properties, it's necessary to copy all of them. If any of those properties are themselves mutable objects, it will be necessary to copy all of those as well. In some cases, making a usable copy of a mutable object's state can be very complicated or even impossible, since objects may have their state intertwined with those of singletons.

Although immutable objects are far easier to "snapshot" than mutable ones, it can sometimes be difficult to, given an immutable object, produce an instance which is similar to the first one except for some minor change. Mutable objects can sometimes be easier to work with in that regard. Sometimes it can be useful to copy data from an immutable object into a mutable object, change it, and then produce a new immutable object which holds the changed data. Unfortunately, there isn't any general means to automate such conversion with classes. There is, however, an alternative.

A struct with exposed fields which are all value primitives or immutable class references can offer a very convenient means of holding information in an immutable object, copying it to a mutable form, modifying it, and then making a new immutable object. Alternatively, one may copy the data in a struct easily by just copying the struct itself. Copying a struct which contains more than one or two int-sized fields is somewhat more expensive than copying an immutable-object reference, but copying a struct and changing it is generally much cheaper than making a new changed immutable object. It's important to note that because of some quirks in the way .net languages handle structs, some people regard mutable structs as evil. I would recommend that in general it's best for structs to simply expose their fields, and avoid having any methods (other than constructors) which mutate this. That will avoid most of the quirks associated with mutable structs, and will often offer better performance and clearer semantics than can be obtained with immutable classes, mutable classes, or so-called immutable (really "mutable by assignment only") structs.

Incidentally, it is sometimes useful to design an immutable object so that producing a slightly-different object will copy everything from the old object to a new one, but with the appropriate part changed, and it is sometimes useful to design an immutable object so that a slightly-different object will be able to "reuse" most of the original object. In many cases, the former approach will be the more efficient one if the object will be read much more often than it is written, and there isn't going to be any need to keep old snapshots around after an object has been changed. The latter approach can be the more efficient one in cases where one will want to keep around many snapshots, and updates are frequent relative to read accesses.


That's not an immutable object. An immutable version of this would be something like

class ImmutableSomething : ISomething
{
    public readonly int SomeInt;

    public ImmutableSomething(int i)
    {
        SomeInt = i;
    }

    public ImmutableSomething AddValue(int add)
    {
        return new ImmutableSomething(this.SomeInt + add);
    }
}

The main benefit of an immutable object is that the object itself will never change, so you don't risk one part of your code changing the underlying values, especially in multithreading situations, but this applies in general. These guarantees often makes objects "better" in that you know what to expect, but there's nothing that makes immutables inherently "faster" than mutable objects.

For example, DateTimes are immutable, so you can do stuff like

DateTime someReferenceTime = DateTime.Now;

myBusinessLayer.DoABunchOfProcessingBasedOnTime(someReferenceTime);

// Here you are guaranteed that someReferenceTime has not changed, and you can do more with it.

Versus something like

StringBuilder sb = new StringBuilder("Seed");

myBusinessLayer.DoStuffBasedOnStringBuilder(sb);

// You have no guarantees about what sb contains here.


Leaving aside the point that the example doesn't actually show an immutable object, the main benefit for immutable objects is that they make certain multi-threaded operations dead simple and lock-free. For example, enumerating an immutable tree structure is possible without locks in a multi-threaded environment, whereas if the tree was mutable, you would need to introduce locks in order to safely enumerate it.

But there is nothing magical about immutable objects that makes them inherently faster.

0

精彩评论

暂无评论...
验证码 换一张
取 消