Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>Use this <a href="https://gist.github.com/1266453" rel="nofollow">T4 template</a> I put together to solve this problem. It should generally suit your needs for whatever kinds of immutable objects you need to create.</p> <p>There's no need to go with generics or use any interfaces. For my purposes, I do not want my immutable classes to be convertible to one another. Why would you? What common traits should they share that means they should be convertible to one another? Enforcing a code pattern should be the job of a code generator (or better yet, a nice-enough type system to allow you to do define general code patterns, which C# unfortunately does not have).</p> <p>Here's some example output from the template to illustrate the basic concept at play (nevermind the types used for the properties):</p> <pre><code>public sealed partial class CommitPartial { public CommitID ID { get; private set; } public TreeID TreeID { get; private set; } public string Committer { get; private set; } public DateTimeOffset DateCommitted { get; private set; } public string Message { get; private set; } public CommitPartial(Builder b) { this.ID = b.ID; this.TreeID = b.TreeID; this.Committer = b.Committer; this.DateCommitted = b.DateCommitted; this.Message = b.Message; } public sealed class Builder { public CommitID ID { get; set; } public TreeID TreeID { get; set; } public string Committer { get; set; } public DateTimeOffset DateCommitted { get; set; } public string Message { get; set; } public Builder() { } public Builder(CommitPartial imm) { this.ID = imm.ID; this.TreeID = imm.TreeID; this.Committer = imm.Committer; this.DateCommitted = imm.DateCommitted; this.Message = imm.Message; } public Builder( CommitID pID ,TreeID pTreeID ,string pCommitter ,DateTimeOffset pDateCommitted ,string pMessage ) { this.ID = pID; this.TreeID = pTreeID; this.Committer = pCommitter; this.DateCommitted = pDateCommitted; this.Message = pMessage; } } public static implicit operator CommitPartial(Builder b) { return new CommitPartial(b); } } </code></pre> <p>The basic pattern is to have an immutable class with a nested mutable <code>Builder</code> class that is used to construct instances of the immutable class in a mutable way. The only way to set the immutable class's properties is to construct a <code>ImmutableType.Builder</code> class and set that in the normal mutable way and convert that to its containing <code>ImmutableType</code> class with an implicit conversion operator.</p> <p>You can extend the T4 template to add a default public ctor to the <code>ImmutableType</code> class itself so you can avoid a double allocation if you can set all the properties up-front.</p> <p>Here's an example usage:</p> <pre><code>CommitPartial cp = new CommitPartial.Builder() { Message = "Hello", OtherFields = value, ... }; </code></pre> <p>or...</p> <pre><code>CommitPartial.Builder cpb = new CommitPartial.Builder(); cpb.Message = "Hello"; ... // using the implicit conversion operator: CommitPartial cp = cpb; // alternatively, using an explicit cast to invoke the conversion operator: CommitPartial cp = (CommitPartial)cpb; </code></pre> <p>Note that the implicit conversion operator from <code>CommitPartial.Builder</code> to <code>CommitPartial</code> is used in the assignment. That's the part that "freezes" the mutable <code>CommitPartial.Builder</code> by constructing a new immutable <code>CommitPartial</code> instance out of it with normal copy semantics.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload