• Stars
    star
    160
  • Rank 234,703 (Top 5 %)
  • Language
    C#
  • License
    Other
  • Created almost 12 years ago
  • Updated over 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Code generation for immutable types

ImmutableObjectGraph

Build Status NuGet package Join the chat at https://gitter.im/AArnott/ImmutableObjectGraph

This project offers code generation that makes writing immutable objects much easier. For instance, the following mutable class:

public class Fruit {
    public string Color { get; set; }
    public int SkinThickness { get; set; }
}

Is very short, easily written and maintainable. The equivalent immutable type would require methods offering mutation, and ideally several other support methods and even a Builder class for use in conveniently handling the immutable object when mutation by creating new objects may be required. These codebases for immutable objects can be quite large.

To reduce the burden of writing and maintaining such codebases, this project generates immutable types for you based on a minimal definition of a class that you define.

Supported features

  • Field types may be value or reference types.
  • When field types are collections, immutable collections should be used that support the Builder pattern.
  • When field types refer to other types also defined in the template file, an entire library of immutable classes with members that reference each other can be constructed.
  • Batch property changes can be made with a single allocation using a single invocation of the With method.
  • Builder classes are generated to allow efficient multi-step mutation without producing unnecessary GC pressure.
  • Version across time without breaking changes by adding Create and With method overloads with an easy application of [Generation(2)].

Usage

You can begin using this project by simply installing a NuGet package:

Install-Package ImmutableObjectGraph.Generation -Pre

On any source file that you use the [GenerateImmutable] attribute in, set the Custom Tool property to: MSBuild:GenerateCodeFromAttributes

Example source file

[GenerateImmutable]
partial class Fruit
{
    readonly string color;
    readonly int skinThickness;
}

Example generated code

The following code will be generated automatically for you and added to a source file in your intermediate outputs folder:

partial class Fruit
{
    [System.Diagnostics.DebuggerBrowsableAttribute(System.Diagnostics.DebuggerBrowsableState.Never)]
    private static readonly Fruit DefaultInstance = GetDefaultTemplate();
    private static int lastIdentityProduced;
    [System.Diagnostics.DebuggerBrowsableAttribute(System.Diagnostics.DebuggerBrowsableState.Never)]
    private readonly uint identity;
    protected Fruit(uint identity, System.String color, System.Int32 skinThickness, bool skipValidation)
    {
        this.identity = identity;
        this.color = color;
        this.skinThickness = skinThickness;
        if (!skipValidation)
        {
            this.Validate();
        }
    }

    public string Color
    {
        get
        {
            return this.color;
        }
    }

    public int SkinThickness
    {
        get
        {
            return this.skinThickness;
        }
    }

    internal protected uint Identity
    {
        get
        {
            return this.identity;
        }
    }

    public static Fruit Create(ImmutableObjectGraph.Optional<System.String> color = default(ImmutableObjectGraph.Optional<System.String>), ImmutableObjectGraph.Optional<System.Int32> skinThickness = default(ImmutableObjectGraph.Optional<System.Int32>))
    {
        var identity = ImmutableObjectGraph.Optional.For(NewIdentity());
        return DefaultInstance.WithFactory(color: ImmutableObjectGraph.Optional.For(color.GetValueOrDefault(DefaultInstance.Color)), skinThickness: ImmutableObjectGraph.Optional.For(skinThickness.GetValueOrDefault(DefaultInstance.SkinThickness)), identity: identity);
    }

    public Fruit With(ImmutableObjectGraph.Optional<System.String> color = default(ImmutableObjectGraph.Optional<System.String>), ImmutableObjectGraph.Optional<System.Int32> skinThickness = default(ImmutableObjectGraph.Optional<System.Int32>))
    {
        return (Fruit)this.WithCore(color: color, skinThickness: skinThickness);
    }

    static protected uint NewIdentity()
    {
        return (uint)System.Threading.Interlocked.Increment(ref lastIdentityProduced);
    }

    protected virtual Fruit WithCore(ImmutableObjectGraph.Optional<System.String> color = default(ImmutableObjectGraph.Optional<System.String>), ImmutableObjectGraph.Optional<System.Int32> skinThickness = default(ImmutableObjectGraph.Optional<System.Int32>))
    {
        return this.WithFactory(color: ImmutableObjectGraph.Optional.For(color.GetValueOrDefault(this.Color)), skinThickness: ImmutableObjectGraph.Optional.For(skinThickness.GetValueOrDefault(this.SkinThickness)), identity: ImmutableObjectGraph.Optional.For(this.Identity));
    }

    static partial void CreateDefaultTemplate(ref Template template);
    private static Fruit GetDefaultTemplate()
    {
        var template = new Template();
        CreateDefaultTemplate(ref template);
        return new Fruit(default(uint), template.Color, template.SkinThickness, skipValidation: true);
    }

    partial void Validate();
    private Fruit WithFactory(ImmutableObjectGraph.Optional<System.String> color = default(ImmutableObjectGraph.Optional<System.String>), ImmutableObjectGraph.Optional<System.Int32> skinThickness = default(ImmutableObjectGraph.Optional<System.Int32>), ImmutableObjectGraph.Optional<uint> identity = default(ImmutableObjectGraph.Optional<uint>))
    {
        if ((identity.IsDefined && identity.Value != this.Identity) || (color.IsDefined && color.Value != this.Color) || (skinThickness.IsDefined && skinThickness.Value != this.SkinThickness))
        {
            return new Fruit(identity: identity.GetValueOrDefault(this.Identity), color: color.GetValueOrDefault(this.Color), skinThickness: skinThickness.GetValueOrDefault(this.SkinThickness), skipValidation: false);
        }
        else
        {
            return this;
        }
    }

#pragma warning disable 649 // field initialization is optional in user code

    private struct Template
    {
        internal System.String Color;
        internal System.Int32 SkinThickness;
    }
#pragma warning restore 649
}

The integration of the code generator support in Visual Studio allows for you to conveniently maintain your own code, and on every save or build of that file, the code generator runs and automatically creates or updates the generated partial class.

Known Issues

When defining more than one immutable type, you may need to keep the arguments to the [GenerateImmutable] attribute consistent for every type. The generator currently assumes that every type has the same arguments as every other type and as a result, for example, generating a Builder from one type and referencing another type, that other type will be assumed to also have a Builder even when it does not, leading to compiler errors.

More Repositories

1

CodeGeneration.Roslyn

Assists in performing Roslyn-based code generation during a build.
C#
408
star
2

IronPigeon

IronPigeon is a decentralized communication protocol that provides high confidentiality and authenticity for the messages.
C#
261
star
3

PCLCrypto

Platform crypto for portable libraries
C#
227
star
4

Validation

Method input validation and runtime checks that report errors or throw exceptions when failures are detected.
C#
127
star
5

Xunit.Combinatorial

Adds combinatorial and pairwise testing capability to Xunit tests
C#
123
star
6

Xunit.SkippableFact

Adds Xunit dynamic skipping of facts and theories.
PowerShell
121
star
7

Library.Template

A template for a NuGet package with tests, stylecop, fxcop, versioning, and Azure Pipelines build ready to go.
PowerShell
114
star
8

Xunit.StaFact

Run your xunit-based tests on an STA thread with the WPF Dispatcher, a WinForms SynchronizationContext, or even a cross-platform generic UI thread emulation with a SynchronizationContext that keeps code running on a "main thread" for that test.
C#
80
star
9

MoneyMan

A financial money management library and applications that utilize it.
C#
39
star
10

SPSS.NET

A .NET library for read/writing SPSS Data (.sav) files. This wraps the functionality exposed by the spssio32.dll native library that comes with SPSS.
C#
31
star
11

StreamJsonRpc.Sample

Sample use of StreamJsonRpc that demonstrate separate client/server processes over named pipes.
C#
28
star
12

Nerdbank.MSBuildExtension

Package to make writing MSBuild extensions easier that work both with MSBuild Core and MSBuild (Full).
C#
23
star
13

PPApiForDotNet

Write PPAPI extensions for Chromium in your favorite .NET language.
C++
20
star
14

CSharpIsNull

C# null test syntax analyzers to guard against bugs in testing null against a struct.
PowerShell
14
star
15

AssemblyRefScanner

Scans a given directory tree for interesting assembly references
C#
14
star
16

Nerdbank.Algorithms

A collection of algorithms I've implemented and found generally useful
C#
13
star
17

Pfx2Snk

Converts PFX files to SNK files.
Visual Basic
12
star
18

DotNetRepoTools

A CLI tool with commands to help maintain .NET codebases
C#
11
star
19

PInvoke.exp

C#
10
star
20

Bitcoin.NET

A .NET Bitcoin library
C#
9
star
21

nerdbank.stylecop.rules

Custom rules for StyleCop
C#
9
star
22

GuidGen

A GuidGen tool similar to the one that ships with VS, but less than a decade old.
PowerShell
8
star
23

ReadOnlySourceTree

A NuGet package that makes your project build to top-level bin and obj directories.
C#
7
star
24

PlugInServer

A network server that can do whatever the live-dropin modules tell it to (HTTP, telnet, ASP.NET, PHP, etc.)
C#
6
star
25

MessagePackDualVersions

Demonstrates how an application can run multiple incompatible versions of a library at once (MessagePack).
C#
5
star
26

Node.js.redist

Builds packages that contain Node.js that can be used to redistribute across platforms
PowerShell
5
star
27

OpenIDForSharepoint

A MembershipProvider for Sharepoint that enabled OpenID logins (forked from CodePlex). BEWARE: Read the README.txt file.
C#
5
star
28

csharplibrarystarterkit

A starting point for C# library development, including logging, tests and build system
JavaScript
4
star
29

YouTubeDownloader

A CLI tool for downloading YouTube videos.
C#
4
star
30

win-buildagent

Docker image build for a windows build agent
Dockerfile
4
star
31

pr-autocomplete-app

A GitHub App that adds auto-complete functionality to pull requests similar to that found in Azure Repos.
TypeScript
4
star
32

dotnetxri

A C# port of the OpenXri4j library
C#
4
star
33

JavascriptCssPackerTargets

An MSBuild-based .targets file and task assembly for minifying js and css embedded resources
C#
4
star
34

cloudbuild-task

Contracts and adapter NPM packages for various cloud build services
TypeScript
4
star
35

PCLCommandBase

A common base class for ICommand implementations, and a BindableBase class for your viewmodels
PowerShell
3
star
36

Nerdbank.Qif

C#
3
star
37

HttpClientEcho

Enables automated tests with HTTP calls to avoid incurring the cost and instability of network traffic by recording a "live" test run and replaying it for subsequent test runs.
C#
3
star
38

dasblog

A private dasblog playground for assembling patches for the official version on CodePlex
C#
3
star
39

StreamingMsgPackSample

A sample of streaming MessagePack from a child process to a parent process
C#
3
star
40

VSIXProjectWithPackageReferences

A sample of a VSIX project that uses PackageReference instead of packages.config. The grass is greener here.
C#
2
star
41

ClueBuddy

A program to run while playing the popular game Clue to help you deduce all the possible clues possible and win
C#
2
star
42

AsyncAndThreadingDemo.Wpf

A simple WPF that shows up async/threading patterns, including JTF
C#
2
star
43

atom2blogger

A blog-to-blog conversion utility for transferring a blog published by Atom feed to Blogger.
C#
2
star
44

Clue

A Clue board game partner to help you deduce clues. Written in Ruby.
Ruby
1
star
45

IdentitySelector

FireFox plug-in for Information Cards support
C++
1
star
46

GoogleAppEngineHiPy

The Hello World app for Google App Engine in Python
Python
1
star
47

setup-dotnet-test

C#
1
star
48

StaticPGO_Example

How to collect a static (not the dynamic one) PGO and re-use it during "dotnet publish"
C#
1
star
49

googauth

Sample .NET web site that uses Google authentication
C#
1
star
50

EnlistmentInfo

A NuGet package that imports EnlistmentInfo .props and .targets from a parent directory.
1
star
51

VSPerfDemo

A repo demonstrating a VS Package that evolves from causing UI delays to a responsive extension.
C#
1
star
52

Nerdbank.Cryptocurrencies

.NET libraries for processing Zcash and other cryptocurrencies.
C#
1
star
53

MyNHibernateContrib

C#
1
star
54

Nerdbank.Algorithms.rs

Rust
1
star
55

pr-autocomplete-scratch

Just a scratch pad for testing PR auto-complete GitHub Actions
PowerShell
1
star