Skip to main content

Immutype by Nikolay Pianikov

Nuget / site data

Nuget GitHub last commit GitHub Repo stars

Details

Info

info

Name: Immutype

Immutable for .NET.

Author: Nikolay Pianikov

NuGet: https://www.nuget.org/packages/Immutype/

You can find more details at https://github.com/DevTeam/Immutype

Source : https://github.com/DevTeam/Immutype

Original Readme

note

Immutype

NuGet

Immutype is .NET code generator creating extension methods for records, structures, and classes marked by the attribute [Immutype.Target] to efficiently operate with instances of these types like with immutable ones.

For instance, for the type Foo for the constructor parameter values of type IEnumerable<int> following extension methods are generated:

  • Foo WithValues(this Foo it, params int[] values) - to replace values by the new ones using a method with variable number of arguments
  • Foo WithValues(this Foo it, IEnumerable<int> values) - to replace values by the new ones
  • Foo AddValues(this Foo it, params int[] values) - to add values using a method with variable number of arguments
  • Foo AddValues(this Foo it, IEnumerable<int> values) - to add values
  • Foo RemoveValues(this Foo it, params int[] values) - to remove values using a method with variable number of arguments
  • Foo RemoveValues(this Foo it, IEnumerable<int> values) - to remove values
  • Foo ClearValues(this Foo it) - to clear all values

For the type Foo for the constructor parameter value of other types, like int, with default value 99 following extension methods are generated:

  • Foo WithValue(this Foo it, int value) - to replace a value by the new one
  • Foo WithDefaultValue(this Foo it) - to replace a value by the default value 99

The extensions methods above are generating automatically for each public or internal type, like Foo marked by the attribute [Immutype.Target] in the static class named as FooExtensions. This generated class FooExtensions is static, has the same accessibility level and the same namespace like a target class Foo. Each generated static extension method has two attributes:

  • [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - to improve performance
  • [Pure] - to indicate that this method is pure, that is, it does not make any visible state changes

Immutype supports nullable reference and value types and the following list of enumerable types:

  • Arrays
  • IEnumerable<T>
  • List<T>
  • IList<T>
  • IReadOnlyCollection<T>
  • IReadOnlyList<T>
  • ICollection<T>
  • HashSet<T>
  • ISet<T>
  • Queue<T>
  • Stack<T>
  • IReadOnlyCollection<T>
  • IReadOnlyList<T>
  • IReadOnlySet<T>
  • ImmutableList<T>
  • IImmutableList<T>
  • ImmutableArray<T>
  • ImmutableQueue<T>
  • IImmutableQueue<T>
  • ImmutableStack<T>
  • IImmutableStack<T>

Immutype supports IIncrementalGenerator as well as ISourceGenerator so it works quite effective.

NuGet package

NuGet

  • Package Manager

    Install-Package Immutype
  • .NET CLI

    dotnet add package Immutype

Development environment requirements

Supported frameworks

Usage Scenarios

Sample scenario

[Immutype.Target]
internal record Person(
string Name,
bool HasPassport = true,
int Age = 0,
ImmutableArray<Person> Friends = default);

public class SampleScenario
{
public void Run()
{
var john = new Person("John", false, 15)
.AddFriends(
new Person("David").WithAge(16),
new Person("James").WithAge(17)
.WithFriends(new Person("Tyler").WithAge(16)));

john.Friends.Length.ShouldBe(2);

john = john.WithAge(16).WithDefaultHasPassport();
john.Age.ShouldBe(16);
john.HasPassport.ShouldBeTrue();

john = john.AddFriends(
new Person("Daniel").WithAge(17),
new Person("Sophia").WithAge(18));

john.Friends.Length.ShouldBe(4);

john = john.RemoveFriends(new Person("David").WithAge(16));

john.Friends.Length.ShouldBe(3);
}
}

Array

[Immutype.Target]
internal readonly record struct Person(string Name, int Age = 0, params Person[] Friends);

public class Array
{
public void Run()
{
var john = new Person("John")
.WithAge(15)
.AddFriends(new Person("David").WithAge(16))
.AddFriends(
new Person("James"),
new Person("Daniel").WithAge(17));

john.Friends.Length.ShouldBe(3);
}
}

Applying defaults

[Immutype.Target]
internal readonly record struct Person(string Name = "John", int Age = 17);

public class ApplyingDefaults
{
public void Run()
{
var john = new Person("David", 15)
.WithDefaultAge()
.WithDefaultName();

john.Name.ShouldBe("John");
john.Age.ShouldBe(17);
}
}

Clearing

[Immutype.Target]
internal readonly record struct Person(
string Name,
int Age = 0,
params Person[] Friends);

public class Clearing
{
public void Run()
{
var john = new Person("John",15, new Person("David").WithAge(16))
.AddFriends(new Person("James"));

john = john.ClearFriends();

john.Friends.Length.ShouldBe(0);
}
}

Immutable collection

[Immutype.Target]
internal readonly struct Person
{
public readonly string Name;
public readonly int Age;
public readonly IImmutableList<Person> Friends;

public Person(
string name,
int age = 0,
IImmutableList<Person>? friends = default)
{
Name = name;
Age = age;
Friends = friends ?? ImmutableList<Person>.Empty;
}
};

public class ImmutableCollection
{
public void Run()
{
var john = new Person("John",15)
.WithFriends(
new Person("David").WithAge(16),
new Person("James").WithAge(17))
.AddFriends(
new Person("David").WithAge(22));

john.Friends.Count.ShouldBe(3);
}
}

Removing

[Immutype.Target]
internal readonly record struct Person(
string Name,
int Age = 0,
params Person[] Friends);

public class Removing
{
public void Run()
{
var john = new Person("John",15, new Person("David").WithAge(16))
.AddFriends(new Person("James"));

john = john.RemoveFriends(new Person("James"));

john.Friends.Length.ShouldBe(1);
}
}

Generic types

It is possible to use generic types including any generic constraints.

[Immutype.Target]
internal record Person<TAge>(string Name, TAge Age = default, IEnumerable<Person<TAge>>? Friends = default)
where TAge : struct;

public class GenericTypes
{
public void Run()
{
var john = new Person<int>("John")
.WithAge(15)
.WithFriends(new Person<int>("David").WithAge(16))
.AddFriends(
new Person<int>("James"),
new Person<int>("Daniel").WithAge(17));

john.Friends?.Count().ShouldBe(3);
}
}

Nullable collection

[Immutype.Target]
internal record Person(
string Name,
int? Age = default,
ICollection<Person>? Friends = default);

public class NullableCollection
{
public void Run()
{
var john = new Person("John",15)
.AddFriends(
new Person("David").WithAge(16),
new Person("James").WithAge(17)
.WithFriends(new Person("Tyler").WithAge(16)));

john.Friends?.Count.ShouldBe(2);
}
}

Set

[Immutype.Target]
internal record Person(
string Name,
int Age = 0,
ISet<Person>? Friends = default);

public class Set
{
public void Run()
{
var john = new Person("John",15)
.AddFriends(
new Person("David").WithAge(16),
new Person("David").WithAge(16),
new Person("James").WithAge(17)
.WithFriends(new Person("Tyler").WithAge(16)));

john.Friends?.Count.ShouldBe(2);
}
}

Record with constructor

[Immutype.Target]
internal record Person
{
public Person(
string name,
int? age = default,
ICollection<Person>? friends = default)
{
Name = name;
Age = age;
Friends = friends;
}

public string Name { get; }

public int? Age { get; }

public ICollection<Person>? Friends { get; }

public void Deconstruct(
out string name,
out int? age,
out ICollection<Person>? friends)
{
name = Name;
age = Age;
friends = Friends;
}
}

public class RecordWithConstructor
{
public void Run()
{
var john = new Person("John",15)
.WithFriends(
new Person("David").WithAge(16),
new Person("James").WithAge(17)
.WithFriends(new Person("Tyler").WithAge(16)));

john.Friends?.Count.ShouldBe(2);
}
}

Explicit constructor choice

[Immutype.Target]
internal readonly struct Person
{
public readonly string Name;
public readonly int Age;
public readonly IImmutableList<Person> Friends;

// You can explicitly select a constructor by marking it with the [Immutype.Target] attribute
[Immutype.Target]
public Person(
string name,
int age = 0,
IImmutableList<Person>? friends = default)
{
Name = name;
Age = age;
Friends = friends ?? ImmutableList<Person>.Empty;
}

public Person(
string name,
int age,
IImmutableList<Person>? friends,
int someArg = 99)
{
Name = name;
Age = age;
Friends = friends ?? ImmutableList<Person>.Empty;
}
};

public class ExplicitConstructorChoice
{
public void Run()
{
var john = new Person("John",15)
.WithFriends(
new Person("David").WithAge(16),
new Person("James").WithAge(17))
.AddFriends(
new Person("David").WithAge(22));

john.Friends.Count.ShouldBe(3);
}
}

About

note

Immutable from constructors

How to use

Example ( source csproj, source files )

This is the CSharp Project that references Immutype

<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Immutype" Version="1.0.14" OutputItemType="Analyzer" >
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
</PropertyGroup>

</Project>

Generated Files

Those are taken from $(BaseIntermediateOutputPath)\GX

// ReSharper disable CheckNamespace
// ReSharper disable ClassNeverInstantiated.Global
namespace Immutype
{
using System;

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor, Inherited = false)]
public class TargetAttribute: Attribute { }
}

Usefull

Download Example (.NET C# )

Share Immutype

https://ignatandrei.github.io/RSCG_Examples/v2/docs/Immutype

In the same category (EnhancementClass) - 24 other generators

ApparatusAOT

AspectGenerator

CommonCodeGenerator

CopyTo

DudNet

FastGenericNew

GeneratorEquals

HsuSgSync

Ling.Audit

Lombok.NET

M31.FluentAPI

MemoryPack

Meziantou.Polyfill

Microsoft.Extensions.Logging

Microsoft.Extensions.Options.Generators.OptionsValidatorGenerator

Microsoft.Interop.JavaScript.JSImportGenerator

OptionToStringGenerator

RSCG_Decorator

RSCG_UtilityTypes

StaticReflection

SyncMethodGenerator

System.Runtime.InteropServices

System.Text.RegularExpressions

TelemetryLogging