Creating an immutable value object in C# – Part V – Using a library


Other posts:

  • Part I – Using a class
  • Part II – Making the class better
  • Part III – Using a struct

  • Part IV – A class with a special value

  • In the last post we presented a variation of implementing a value object using a class. Everything works (obviously), but the amount of code to write is unpleasing. In this post we examine a library based solution. I just describe how to use the Record class, not how it is implemented. You can read the attached implementation code (it is in functional.cs). There is much more in there than “Record<…>”. I’ll talk about the rest in a (hopefully) upcoming series.


    To use the record class you need to inherit from it, as in:

    public class DateSpan: Record<DateTime, DateTime, bool> {…}

    The generic argument types represent the types that comprise the (immutable) state of the object. You then need a friendly way for folks to access this state:

        public DateTime Start { get { return state.Item1; } }
    public DateTime End { get { return state.Item2; } }
    public bool HasValue { get { return state.Item3; } }

    This is all you have to do. You don’t need to implement “Equals”, “==”, “!=” and “GetHashCode”. Structural equivalence is given to you by the Record class. Such a property is recursive, in the sense that you can embed value objects inside other value objects and the implementation would walk your object graph as necessary.


    For example, given the following class hierarchy:

        public class Order: Record<string, int> {

    public Order(string item, int qty): base(item,qty) {}

    public string Item { get { return state.Item1;}}
    public int Quantity { get { return state.Item2; } }
    }

    public class Customer: Record<string, IEnumerable<Order>> {

    public Customer(string name, IEnumerable<Order> orders) : base(name, orders) { }

    public string Name { get { return state.Item1; } }
    public IEnumerable<Order> Orders { get { return state.Item2; } }
    }


    The following test case succeed:

            [TestMethod]
    public void Record2Test() {

    var c1 = new Customer(“Luca”, new Order[] { new Order(“car”,1), new Order(“stereo”, 3)});
    var c11 = new Customer(“Luca”, new Order[] { new Order(“car”, 1), new Order(“stereo”, 3) });
    var c2 = new Customer(“Bob”, new Order[] { new Order(“car”, 1), new Order(“stereo”, 3) });
    var c3 = new Customer(“Bob”, new Order[] { new Order(“car”, 1), new Order(“stereo”, 2) });

    Assert.AreEqual(c1, c11);
    Assert.AreNotEqual(c1, c2);
    Assert.AreNotEqual(c1, c3);
    Assert.AreNotEqual(c2, c3);

    }


    Please don’t take my library as production ready code. The amount of test I put into it is limited. You can probably find obvious bugs with it.


    Let’s look at other drawbacks. The biggest one is that I’m stealing your base class. If you want your value object to inherit from something else, you cannot. You cannot even have value objects inherit from each other. In that case you are back to implementing your own Equals, == and so on. The only tools at your disposal are interfaces and composition.


    Another drawback is that writing classes in this way is slightly unnatural. You have to think about the ‘type’ of your state in the declaration of the class itself instead of more naturally writing it closer to where you assign names to it (property/field declaration).


    Having considered these drawbacks, I’m using this library in all my code wherever I need value objects (which is almost everywhere these days). Writing all the “Equals” explicitly is too error prone for my taste. I will also be creating IDE snippets for myself that make writing these classes easier.


    I don’t think I have anything else to say on this topic, so this will be my last post on it. If something else comes up, I’ll let you know.

    TimeLineUsingRecord.zip

    Comments (12)

    1. For some reason, there’s been a lot of buzz lately around immutability in C#. If you’re interested in

    2. Welcome to the XXXIX issue of Community Convergence. The big news this week is that Microsoft has begun

    3. mg says:

      Nice work!

      One question about "stealing" the base class – what about introducing another layer of abstraction and instead of declaring your type like this:

      public class DateSpan: Record<DateTime, DateTime, bool> {…}

      do something like this:

      public class DateSpan: ValueObject<Record<DateTime, DateTime, bool>> {…}

      The ValueObject class would then expose the record to as a protected property and pass Equals, ToString… to the Record object. It would make the declarations a little bit more complicated, but your biggest drawback is not a problem anymore.

    4. lucabol says:

      I think I’m missing something. Aren’t you still stealing the base class with the ValueObject class?

      I.E. you cannot declare DateSpan to inherit from a MySpan class.

    5. mg says:

      Well, I was thinking about ValueObject more in terms of a type system base class, where you can put your additional behaviour and not spoil the Record class. You are right that still you cannot have a deeper hierarchy of types with this solution.

      BTW: don’t you think it would be nice to have a language support for immutable types? Something like:

      public readonly class MyClass…

      where all fields are readonly fields of readonly types?

    6. lucabol says:

      Yeah, you are right.

      We talked about having first class readonly classes a bunch of times, but we never came up with a proposal we are happy with. We are still discussing the topic.

    7. Doekman says:

      Isn’t code generation a much better option at this time, while 1st class readonly classes aren’t available?

    8. lucabol says:

      It is an option. I wouldn’t say it is better. It has pros and cons (i.e. readibility, amount of code, mantainability).

      As for me, I prefer library solutions to codegen whenever available (and roughly usable).

    9. qq says:

      I think you guys in immutable space have frankly lost it. It is not new, it is common principles that do not apply in all fields, and just because functional attempts are again popular you are hitting on attempting to solve a problem with a wrong tool: .net type system.

      If you think magically you will somehow parallelise code and algorithms, you are in for a big ‘immutable surprise’.

      No, silver, bullet..

    10. lucabol says:

      All these posts say is: iff you need an immutable class, here are a bunch of options of how to do it in C#.

      I don’t think I’m claiming anything more than that. Am I?

    11. Previous posts: Part I – Background Tuples are a way for you not to name things. In Object Oriented languages