Stidgen is an old (4 years) project of mine that was created to fill a professional need but that I never really advertised until now. The name stands for “Strongly Typed ID types GENerator”.

What it does is to generate C# types to wrap simple types used as identifiers (string -> UserId, Guid -> TweetId, …) to have nicely typed code.

A simple .stidgen file (The format is documented on the readme) with the following content :

public UserId<string>
public CompanyId<string>
public UploadedFileId<Guid>

Generate a 837 lines file, the types can then be used to get readable type signatures :

interface IPopulationQueries
{
    UserInfo GetUserInfo(UserId id);
    CompanyId GetUserCompany(UserId id);
    IReadOnlyCollection<UserId> GetCompanyUsers(CompanyId id);
}

Usage

The latest version of stidgen is available as a dotnet code (2.1+) tool. It can be installed globally like that :

dotnet tool install --global stidgen

and can the be used from anywhere :

stidgen samples/Simple.stidgen

As a dotnet tool it can also be installed in a project and used from CI/FAKE. (No official FAKE module yet, it’ll come)

What ID types provide

A very simple wrapper type can be generated by hand in a dozen of lines but stidgen provide a lot more features by default :

  • An immutable structure with a single member, something that the JIT can recognize and optimize away.
  • Casts from/to the underlying types.
  • Equality members, interfaces and operators.
  • Interning of string when it’s the underlying type, with nice optimizations to Equal and GetHashCode. I’ll try to blog about them it’s quite an interesting optimization.
  • ToString() and formattable overloads are lifted.
  • DebuggerDisplay is present to simplify debugging.
  • Optional protobuf-net attributes generation.

Comparison with F# solution

While stidgen is written in F# it doesn’t provide a way to generate ID types in F#. One of the reason is that generating formatted F# code is quite hard, but the other is that there is already 80% of the features directly available in the language.

The solution to the same problem in F# is to use a single-case discriminated union:

[<Struct>]
type UserId = | UserId of string

[<Struct>]
type CompanyId = | CompanyId of string

[<Struct>]
type UploadedFileId = | UploadedFileId of Guid

And F# nicely provide most of what stidgen does by default (Equality interfaces and operators, immutability) but there are still a few missing things so maybe one day i’ll work on a F# generator :

  • No way to automatically intern strings
  • No interning-specific optimizations.
  • No check for null (Producing nulls in F# is rare but .NET corelib/libraries can produce them)
  • ToString isn’t handled
  • No DebuggerDisplay

Note: Stidgen generated types works very nicely from F#

The code

Stidgen is an F# project that uses Roslyn the C# compiler platform to generate nicely formatted code automatically.

The FluentRoslyn.fs file in the respository contains an F# wrapper for Roslyn code generation that produce code like that :

if'
    (equals info.ThisValueMemberAccess Literal.Null)
    (block
        [|
            ret
                (invocation
                    (dottedMemberAccess' ["System"; "Convert"; m.Name])
                    [| Literal.Null |])
        |])

Conclusion

Stidgen is a project that we use extensively at work but I feel that we might not be the only ones that want strongly typed code in a C# codebase. So if you have this need or a similar one, why not try it ?