go2cs

go2cs — Go to C# Converter

golib NuGet package

Browse all: Go Standard Library NuGet packages


📰 NEWS — Every implementable standard-library package validates

All 225 implementable packages of Go 1.24.13’s standard library pass their own test suites in C#: 225 of the 230 testable (97.8%), at 69,777 matching verdicts against go test -json, with 373 divergences disclosed by exact failure signature. They include runtime itself, reflect, net/http and crypto/tls, and on Linux 223 of the 223 applicable rows validate. Each row of the validated roster links a proof page that lists Go’s verdict beside go2cs’s, test by test, and the five packages outside the 225 are listed with their reasons. The converted library ships as NuGet 1.24.13.3, targeting .NET 10. The full announcement has the details.

➡ All announcements can be found in the go2cs News Archive.

go2cs Purpose

Convert source code written in the Go programming language into C#. The generated C# is designed to be both behaviorally and visually similar to the original Go — so a Go developer can read the converted code and follow it easily, and a .NET developer can use Go code directly within the .NET ecosystem.

Tour of go2cs showing Go and generated C# side by side

Frequently asked questions

Transpiler Goals

Go leans on its compiler and runtime for slices, maps, channels, goroutines, defer/panic/recover, multiple returns, struct embedding and interface duck-typing. go2cs maps each onto idiomatic C#, keeping the machinery out of sight — in a small runtime library and compile-time source generators — so the converted code stays close to the original Go.

Example

Given this Go:

type Person struct {
    name string
    age  int32
}

func (p Person) IsAdult() bool {
    return p.age >= 18
}

go2cs produces this C#:

[GoType] partial struct Person {
    internal @string name;
    internal int32 age;
}

public static bool IsAdult(this Person p) {
    return p.age >= 18;
}

Real standard-library conversions, side by side

The goal — reads like Go — is easiest to judge on real code. Below are converted standard-library files next to their original Go 1.24.13 source, in order of increasing richness:

Package Go 1.24.13 source Converted C# What it shows
errors errors.go errors.cs Error values and an unexported type satisfying the error interface.
cmp cmp.go cmp.cs Generics with an ordered-type constraint.
unicode/utf8 utf8.go utf8.cs Constants keeping Go’s hex/binary literal formatting; arrays and structs.
sort search.go search.cs Binary search driven by a func(int) bool closure.
strings reader.go reader.cs A struct with receiver methods, tuple returns, and interface implementation.
container/list list.go list.cs A doubly-linked list — pointers and receiver methods.

Browse the whole set under src/core.

Features

go2cs converts the full Go language surface except a generic type alias whose target is an anonymous struct or interface type (type A[T any] = struct{…}), which it reports and does not yet convert — the same converter that emits the packages above:

Types & values

Functions & methods

Concurrency

Composition & polymorphism

Control flow & packaging

See ConversionStrategies.md for an example-driven tour of how each construct maps to C# (with ConversionStrategies-Reference/ for the full detail).

GopherDotNetBotFrisbee

Requirements

Installing the converter

Build the go2cs executable from source and place it on your PATH. The simplest way is go install, which compiles it and drops the binary into %GOBIN% (or %GOPATH%\bin) — already on your PATH in a standard Go setup — in one step:

cd src/go2cs
go install .

Go produces a self-contained native binary. To target another platform, use Go’s standard cross-compilation (GOOS/GOARCH).

Usage

go2cs [options] <input_dir> [output_dir]

Examples:

go2cs example.go                       # convert a single file
go2cs package_dir                      # convert a package
go2cs -indent 2 -var=false example.go conv/example.cs
go2cs -stdlib                          # convert the entire Go standard library
go2cs -stdlib fmt strings io           # convert specific standard library packages
go2cs -recurse=nuget module_dir out    # convert a module + its third-party deps, stdlib from NuGet
go2cs -recurse module_dir              # same, referencing a locally-staged standard library
go2cs -recurse module_dir output_root  # ...with the generated app/dep trees under output_root
go2cs -recurse=module module_dir out   # convert only the module's own packages (deps referenced, not converted)
go2cs -tests package_dir               # convert a package plus its Go test suite
go2cs -tests -test-action all goroot_pkg_dir converted_pkg_dir   # ...and build, run, and diff vs go test
go2cs -tests -recurse -test-action all module_dir out_root      # validate a whole module against its own tests

Common options

Option Description
-stdlib Convert the Go standard library (optionally followed by specific package names).
-recurse Convert a downloaded module and its third-party dependencies in dependency order, referencing (not reconverting) the pre-converted standard library. An optional second positional sets the output root for the generated src\ (app) and pkg\ (dependency) trees. A package that fails to convert is reported and skipped. See Converting a real-world module.
-recurse=module Same, but convert only the module’s own packages: third-party packages are referenced into pkg\ but not converted, and are listed at the end of the run. See converting the module only.
-recurse=nuget Same, but the standard library, the golib runtime and the analyzer come from NuGet (go.<pkg>, go.lib, go.gen), so nothing is staged locally. Third-party modules that the nugetgo.net registry maps are referenced as published packages when a qualifying version exists, and converted locally otherwise (see Mapping modules to NuGet packages). Scope and reference style combine: -recurse=module,nuget. The published packages match the Go release go2cs is built with; go2cs checks the module against that release first, and refuses a mismatch, naming both versions.
-nuget-map <source> With -recurse=nuget: add a mapping source, a local file or an https:// URL. Repeatable; the first source that names a module wins, and nugetgo.net answers the rest. -nuget-map off turns mapping off. See Mapping modules to NuGet packages.
-nuget-map-only With -nuget-map: use only the listed sources — the nugetgo.net fallback is dropped, so a module your sources do not name stays local, and nugetgo.net is never fetched.
-nuget-map-exclude <module-path> With -recurse=nuget: never map this module, whatever a source says. Repeatable.
-nuget-map-refresh With -recurse=nuget: fetch every URL source unconditionally (bypassing the local cache) and re-resolve, adopting what the sources say now instead of what go2cs.nuget.lock pinned.
-nuget-map-canonical-only With -recurse=nuget: apply only canonical mappings (the module owner’s own conversion); a community mapping is treated as unmapped.
-tests Also convert the package’s _test.go suite and emit a runnable C# test-host project (default off). Forces -comments on and works from a bare clone with no flags or environment setup. With plain -recurse it validates a whole module against its own tests: go2cs -tests -recurse module_dir out_root, with the output root outside the module’s source tree; a module that needs a newer Go than the converted standard library is refused. See Try it yourself.
-test-action <action> With -tests: one of convert (default), build, run, compare, or all. convert and all convert the package and its tests; build / run / compare act on the existing converted artifacts — validated against the test manifest’s recorded input digest — without reconverting. compare (and all) runs both go test -json -count=1 and the converted C# test host and diffs the terminal results by test name.
-test-timeout <duration> Package deadline for a converted-test action, in Go duration syntax (default 2m); run and compare give it to both go test and the converted host. The host’s dotnet publish always gets at least 30m, because the first publish on a fresh tree builds the whole standard-library closure. A suite that runs long in C# needs a larger value: hash/maphash is validated with -test-timeout 30m.
-go2cspath <dir> Runtime/stdlib root (env GO2CSPATH; default ~/go2cs) used by generated $(go2csPath)… references, and the output root for -stdlib. For a single-package/file conversion, C# output goes to optional [output_dir] (in place by default).
-goroot / -gopath Override the detected Go root / path.
-platforms <os/arch> Target platform for build-tagged files (defaults to the host). A comma-separated list (windows/amd64,linux/amd64,darwin/amd64) is accepted and today requires -platform-census: a conversion still emits for exactly one target, so a list without the census flag is rejected rather than silently converting the first.
-platform-census <dir> With -stdlib and two or more -platforms targets: convert once per target into an isolated, seeded staging root under <dir>, compare what each run actually emitted, and write <dir>\platform-manifest.json classifying every artifact as shared, variant, partial or platform-exclusive. Produces no converted output of its own — -go2cspath is read as the seed and never written to.
-tags <list> Build tags applied when loading packages. -stdlib and -tests apply purego by default; an explicit value replaces it.
-indent <n> Spaces per indent level (default 4).
-var Prefer var declarations where the type is obvious (default on).
-uco Emit channel operators instead of method calls (default on).
-comments Carry source comments into the output (best effort, see go/ast comment status).
-csproj <file> Generate project files from a custom .csproj template instead of the embedded one.
-tree Print each file’s Go parse tree (go/ast) to stdout during conversion — a diagnostic aid.
-debug Disable the converter’s per-file panic recovery, so a conversion failure crashes with a full stack trace instead of being reported as a warning.
-version Print the converter’s release tuple and exit 0, converting nothing. One key=value line per key, always in this order, every key present (unknown when a value cannot be resolved), so a bug report or a build script can paste or parse it: go2cs.release (the go2cs corpus release this binary belongs to, e.g. 1.24.13.3), go2cs.converter (this binary’s identity, exe- plus the first 16 hex digits of its SHA-256 — the value test manifests record), go.packages (the go.* NuGet version -recurse=nuget defaults $(GoStdLibVersion) to, e.g. 1.24.13.*), go.toolchain (the active Go toolchain, go env GOVERSION), go.build (the Go toolchain that built this binary), vcs.revision and vcs.modified (the commit a binary built from a clone was built from, and whether its tree had local changes; Go 1.24 does not stamp a binary built from a git worktree checkout, so that reads unknown). New keys are only ever appended.
-cgo Also convert cgo-targeted files. Not yet functional, but planned: the cgo interop plan lays out a P/Invoke-backed bridge for import "C", and this flag comes alive with it.

All converted C# code references a hand-written runtime library (golib, published as the go.lib NuGet package) plus a set of Roslyn source generators that supply Go semantics at compile time (published as go.gen). A -recurse=nuget conversion wires both up for you.

Diagnostics

A failure in the Go input itself is printed on stderr in MSBuild’s canonical error format, one line per error, so CI logs and editor problem matchers (for example VS Code’s $msCompile) pick it up:

/src/app/main.go(3,16): error GO2CS1003: cannot use 1 (untyped int constant) as string value in variable declaration
Code Meaning
GO2CS1000 The package did not load cleanly (an error go/packages does not classify). The package still converts best-effort.
GO2CS1001 The package did not load cleanly: a go list error, such as a missing module or no Go files for this build.
GO2CS1002 The package did not load cleanly: a Go parse error. The package still converts best-effort.
GO2CS1003 The package did not fully type-check. It still converts best-effort, but code that depends on the failing expression is emitted untyped and will not compile.
GO2CS2001 Refused: a cgo source (import "C") is selected for this build, and cgo has no C# conversion yet. The run exits non-zero. Convert with CGO_ENABLED=0 or exclude the file.

A diagnostic with no source position names go2cs as its origin. The WARNING: / Refusing to convert: summary line printed before these lines is unchanged.

Converting a real-world module

The -recurse option converts a whole downloaded application together with every third-party dependency package in its transitive import closure — in dependency order (least-dependencies-first) — while referencing (not reconverting) the pre-converted standard library. The result is a C# solution you can open and build. With -recurse=nuget the standard library, the golib runtime and the go2cs-gen analyzer come from nuget.org, so nothing has to be staged on the machine beforehand. (Prefer the standard library as local C# source? See building against a local standard library below.)

Wondering which real-world Go packages make good conversions? The go2cs Target Atlas ranks the most-depended-on Go modules and grades how hard each one is to convert.

Here is the full round-trip for a small CLI that uses github.com/fatih/color, which itself pulls in github.com/mattn/go-colorable, github.com/mattn/go-isatty, and golang.org/x/sys — a genuine dependency graph:

NOTE: the commands below run verbatim in both PowerShell and a POSIX shell (bash/zsh), on Windows and on Linux. Where the two genuinely differ, both forms are shown and the block is labelled with its shell.

1 — Go: get the app and confirm it builds as Go.

mkdir colordemo
cd colordemo
go mod init example.com/colordemo

Create main.go (go mod tidy needs a real source file — with none present it reports warning: "all" matched no packages):

package main

import "github.com/fatih/color"

func main() {
	color.New(color.FgGreen, color.Bold).Println("hello from fatih/color")
}

Next, pin the app’s dependencies to releases that go2cs’s Go version can read, and confirm it builds as Go.

NOTE: go2cs’s type-checker reads only modules whose go directive (and their dependencies’) is no newer than the Go release go2cs is built with (see Requirements). Newer fatih/color and golang.org/x/sys releases need a newer Go, which would fail step 2 with package requires newer Go version; pin as shown.

The GOTOOLCHAIN=local below is what makes that error appear at all. Left unset, Go silently downloads and re-execs whichever newer toolchain a go/toolchain directive asks for, so the build succeeds against a standard library go2cs has no published packages for. go2cs detects the switch and says so, but pinning the toolchain keeps the whole round-trip on one Go release, which is what you want.

First pin the toolchain, so Go uses the release you have instead of fetching the newer one a dependency asks for. This is the one command whose syntax is shell-specific:

$env:GOTOOLCHAIN = 'local'   # PowerShell
export GOTOOLCHAIN=local     # bash / zsh

Then, in either shell:

go get github.com/fatih/color@v1.18.0   # a Go 1.24-compatible release (v1.19+ requires Go 1.25)
go mod tidy                             # download color + its dependencies
go build ./...                          # baseline: confirm it compiles as Go first

2 — go2cs: recurse-convert the app. go2cs is the converter you put on your PATH in Installing the converter above, so it runs from anywhere. Point it at the app directory, and give it an output root to write the generated C# into:

cd path/to/colordemo
go2cs -recurse=nuget . csharp

go2cs discovers the imports and converts each package, least-dependencies-first (go-isatty and x/sys → go-colorable → color → the app), into a parallel tree under csharp/, leaving your original Go source untouched. The converted app lands under csharp/src/<import-path>, and every third-party library under csharp/pkg/<import-path>. The standard library is referenced as go.<pkg> packages, and the generated csharp/Directory.Build.props supplies the version they resolve — so the projects restore and build with no further configuration. A per-project .slnx sits next to every generated .csproj, each with that project plus its converted dependencies. By default a -recurse=nuget run also asks the nugetgo.net registry whether a third-party module has a published conversion, and references one that does as that NuGet package instead of converting it (see Mapping modules to NuGet packages; -nuget-map off opts out). Where Go’s own semantics draw a C# warning in one file (a local whose every use folded to a constant, a package variable nothing writes), an .editorconfig beside that .csproj turns it off for that file only (Conversion Strategies).

Code converted from main.go should look like the following in main.cs:

namespace go.example.com;

using color = github.com.fatih.color_package;
using github.com.fatih;

partial class main_package {

// Hoisted @string literals (single allocation; Go keeps these in RODATA)
private static readonly object helloFromFatihColorˢ = (@string)"hello from fatih/color"u8;

internal static void Main() {
    color.New(color.FgGreen, color.Bold).Println(helloFromFatihColorˢ);
}

} // end main_package

NOTE — platforms: all four steps run on Windows (windows/amd64) and Linux (linux/amd64). The conversion records the platform it targets, and the go2cs packages compile and run against that platform’s flavor: win-x64 for a Windows conversion, linux-x64 for a Linux one. Use the current go2cs packages, with a converter built from a checkout at or after the commit that published them. The output matches go run, including fatih/color’s colors in an interactive terminal, on both platforms. macOS (Intel and Apple silicon): packages ship for both chips, the behavioral suite passes on both, and the README walkthrough runs. The standard library is not yet validated package by package on macOS, so don’t expect it to be fully operational there yet. Other platforms and architectures are tracked in the Roadmap’s Platforms section.

3 — C#: build the generated solution. The app’s per-project .slnx builds the app and its whole converted dependency tree, restoring the go2cs packages on the way; opening it in Visual Studio makes the app the startup project (F5 runs it):

cd csharp/src/example.com/colordemo
dotnet build example.com.colordemo.slnx -c Debug

4 — C#: run the converted app. From the same folder:

dotnet run -c Debug

The build output lands under csharp/.artifacts/bin/, where a native launcher is built beside the assembly — colordemo.exe on Windows, ./colordemo on Linux — and runs the same program.

To ship the app, dotnet publish -c Release -r <runtime> produces a self-contained, ReadyToRun-compiled program. A converted project trims in partial mode: the .NET framework is trimmed, while golib, the converted packages and the app itself are kept whole, so the members they reach by reflection survive the publish. A project may set its own TrimMode.

Expected output:

colorapp-output

NOTE: this fatih/color example compiles clean — app plus all four dependency projects — and runs. The standard library it references is validated package by package against Go’s own tests; see Validated Test Packages.

Optional: convert the module only, and deal with its dependencies later

A dependency closure is not always convertible today — a large third-party SDK can hit a converter defect, or pull in a package go2cs cannot yet handle — and under plain -recurse that blocks the packages you actually came for. -recurse=module narrows the scope to the input module’s own packages:

cd path/to/colordemo
go2cs -recurse=module . csharp

Every package under the module’s own go.mod converts, in dependency order, exactly as it would under the full -recurse; every third-party package is referenced — into csharp/pkg/<import-path>, the same place the full run would have converted it — but never converted, so nothing about it can fail the run. The converter prints the referenced-but-unconverted list when it finishes:

Closure: 73 packages discovered — converting 1 app, referencing 4 third-party + 67 stdlib (1 skipped)
...
Third-party packages referenced but NOT converted (-recurse=module): 4
  github.com/fatih/color
  github.com/mattn/go-colorable
  github.com/mattn/go-isatty
  golang.org/x/sys/windows

Those references are unresolved until something is written at those paths, so the generated solution does not build yet — the mode’s deliberate trade. Re-running the same conversion without =module (once the dependencies convert) writes them at exactly those paths and resolves the references; the app’s own converted .cs and .csproj come out byte-identical either way, so nothing you have done to them is lost.

Optional: build against a local standard library

Some work wants the standard library on disk as C# source instead — to step into it in the debugger, or to change it and rebuild. deploy-core.ps1 is a PowerShell script in the go2cs repo’s src/ folder (it is not on your PATH), so run it from there, from a PowerShell prompt — powershell or pwsh on Windows, pwsh on Linux and macOS. It stages the standard library, runtime and analyzer at <gopath>/src/go2cs — the “deploy root” a converted project resolves through $(go2csPath), where <gopath> is the directory go env GOPATH prints:

cd path/to/go2cs/src
./deploy-core.ps1

Staging is per-machine, not per-app; redo it when you pull a new go2cs version. Then convert with plain -recurse, pointing at the deploy root:

cd path/to/colordemo
go2cs -recurse . -go2cspath <gopath>/src/go2cs

The converted app lands under <gopath>/src/go2cs/src/<import-path> and its converted third-party dependencies under <gopath>/src/go2cs/pkg/<import-path>, with the standard library referenced at <gopath>/src/go2cs/core/; build and run it exactly as in steps 3 and 4 from there. The converted C# is the same either way — only the reference style in the generated projects differs.

The output root does not have to be the deploy root: go2cs -recurse -go2cspath <gopath>/src/go2cs . csharp keeps the generated tree in csharp/ while still referencing the deploy root. The conversion records that root as the $(go2csPath) default in the output root’s generated Directory.Build.props, so the generated solution builds from anywhere with no extra configuration — override with a go2csPath environment variable or a -p:go2csPath build global if the runtime root later moves.

Mapping modules to NuGet packages

With -recurse=nuget, go2cs asks the nugetgo.net registry whether someone has already converted and published each third-party module. A mapped module is referenced, not converted: one exact-pinned PackageReference per module. A mapping applies only when a published version describes itself (in go2cs/source-metadata.txt) as that module, at exactly the version your build selects, and was built for this go2cs release; the highest such revision wins. Anything else converts locally, and the end-of-run table says why. A module supplied by a replace directive is never mapped. A mapped package that itself depends on another third-party package is not yet tested end to end.

The choices are pinned, with each package’s SHA-512, in go2cs.nuget.lock in the output root. A later run reuses the pin offline, warns if a source now disagrees, and refuses a package whose bytes no longer match.

Project layout

Path Contents
src/go2cs/ The converter (written in Go, using go/ast + go/types).
src/core/ The converted Go standard library — every package, with unsafe and testing hand-written rather than converted. Everything (tests, tour, NuGet) builds against this one tree.
src/core/golib/ The C# runtime library (slice, map, channel, @string, built-ins, type aliases).
src/core/go2cs/ Shared Symbols project — the canonical marker glyphs used by the runtime and the generators.
src/gen/go2cs-gen/ Roslyn source generators (interface implementation, receiver overloads, struct embedding).
src/tour/ Tour of go2cs — the Tour of Go beside a live Go-to-C# workspace.
src/tests/Behavioral/ Per-feature Go↔C# equivalence tests (transpile, compile, run-and-compare).
src/tests/Performance/ Go vs transpiled C# runtime benchmarks (JIT and Native AOT) — see the performance comparison for current numbers.

Contributors: start with CONTRIBUTING.md, then see Architecture.md, ConversionStrategies.md and Roadmap.md for details. There’s plenty of low-hanging fruit here; jump in if you’d like to help.

Status

The converter builds idiomatic C# for the full range of Go language features, guarded by hundreds of Go-vs-C# behavioral test projects. Each one is transpiled, compiled and compared against a committed golden, and each runnable one is executed with its output compared against Go’s. The entire Go standard library compiles cleanly as .NET assemblies.

The converted standard library reproduces Go built with -tags purego — a managed runtime cannot execute Go’s hand-written .s assembly, so the portable pure-Go variants of the asm-backed crypto and hash functions are the faithful target (-stdlib and -tests apply the tag by default; see Conversion Strategies).

Compiling is not the same as running correctly, so the library is also validated operationally. Each package’s own _test.go suite is converted to C#, built against the converted standard library, and run under a Go-semantics test host. Its results are compared verdict for verdict against a clean go test -json run, and every difference is disclosed by exact failure signature; a test withdrawn is withdrawn from both sides, by name. Every implementable package validates this way, on Windows and on Linux where it applies, and the few packages outside that set are each listed with the reason they cannot be validated. Validated Test Packages carries the current counts, and the results are reproducible via Try it yourself. Real third-party Go modules convert too, and go2cs -tests -recurse validates a module against its own test suites the same way. go2cs moves to newer Go and .NET releases one at a time, and each move re-validates every package against that release’s own tests; see the Roadmap.

Try it yourself — validate a converted test suite

Every validated package ships its converted C# test sources next to the production code under src/core (for example, unicode/utf8/utf8_test.cs), so you can read the exact C# that runs — and re-run the validation yourself. You need Go 1.24.13 (for the reference go test run), the .NET 10 SDK, and go2cs on your PATH (see installing the converter):

# 1. Convert unicode/utf8's test suite, build + run the C# host, and diff it against `go test`.
#    The second argument is the package's home in the converted tree; the converter locates the
#    runtime and its stdlib dependencies from there — no flags or environment setup required.
#    (On Windows, Go's source lives under "C:\Program Files\Go\src"; elsewhere use "$(go env GOROOT)/src".)
#    Off Windows the converter binary is `go2cs`, not `go2cs.exe`.
#    The first run builds the converted runtime and its dependencies, so it allows 10 minutes.
go2cs.exe -tests -test-action all -test-timeout 10m \
    "C:\Program Files\Go\src\unicode\utf8" \
    src/core/unicode/utf8

Expected final line:

Validated 14 tests against go test (0 skipped identically on both sides, 1 disclosed-divergent (deferred), 47 disclosed-unsupported declarations excluded).

The command converts the _test.go files to C#, generates a test host, builds it against the converted standard library, runs it in an isolated process, captures a clean go test -json -count=1 baseline, and compares terminal results by full Go test name — reporting validated only when every test agrees on both sides and every unsupported declaration (benchmarks, examples) is accounted for. It regenerates the local converted .cs in place; the Go source copies and run manifests it stages are git-ignored. The same command validates every other package on the table — substitute its GOROOT source path and its src/core/<pkg> path in the two arguments.

The test host is published in the Release configuration and run with the CLR’s tiered JIT off (DOTNET_TieredCompilation=0), so every method is compiled once at full optimization and a verdict cannot depend on when the runtime promotes a method; -test-tiered turns tiering back on, and -test-config Debug publishes a Debug host. ReadyToRun is opt-in — set PublishReadyToRun=true in the environment to publish a precompiled host — because it buys a slower steady state on heavy suites, a larger host and a longer first publish, and because precompiled code does not keep the JIT’s stack frames, so a test that asserts on frames can answer differently.

A few packages carry a disclosed divergence: a Go test asserting something the converted runtime does not satisfy — an allocation count Go meets through compiler escape analysis, or a collectibility check Go answers from per-safepoint liveness maps. Some of these a managed runtime provably cannot satisfy; others — marked deferred, such as unicode/utf8’s own zero-allocation TestRuneCountNonASCIIAllocation — it can, and each of those is pinned against the named plan that will retire it. Rather than skip those tests, each affected package pins the divergence in a hand-owned, committed go2cs_test_disclosures.json that the differential oracle matches by exact failure signature — any other failure is still a hard mismatch — and reports as disclosed-divergent in the summary. Packages without a manifest compare strictly.

Performance

Everyone asks: how fast is the transpiled C# compared to the original Go — including startup time, memory, and Native AOT builds? See the performance comparison — TL;DR: usually slower than native Go, but not always: maps and the optimized stack string path run at parity with Go or faster in both C# variants. Most compute-shaped code — channels included — sits within a small multiple of Go, with runtime structural-interface satisfaction the honest outlier. Some optimization is already in, such as the ref struct based stack strings and stack slices; broader optimization is planned (see the Roadmap), and the parity rows show the ceiling, not the finish line.

Milestones

High level timeline of the project’s major turning points; the tags carry the details.

Date Milestone Commit / Tag Notes
2018-05-21 Project inception 929d1457f A C#/.NET converter built on an ANTLR4 Go grammar.
2022-03-13 v0.1.2 release v0.1.2 The mature ANTLR4-era converter, tagged.
2025-01-12 The converter is rewritten in Go 87465f5f5 Rebuilt on go/ast + go/types, with the golib runtime library and Roslyn source generators supplying Go’s semantics.
2025-05-05 First full standard-library auto-conversion 6ca1c45b7 · full-conversion-2025-05 (cc14584c7) Every Go file gets a C# file; compiling comes later.
2026-07-10 First clean full-standard-library compile 51ba5d9cf · stdlib-green-2026-07-10 Every package compiles with zero errors, runtime, reflect and net/http included (details).
2026-07-14 Standard library on NuGet 2363af0e6 · dd821a556 · nuget-stdlib-2026-07-14 A converted app references the standard library from nuget.org, with no local go2cs checkout.
2026-07-17 First Go test suite passing in C# 337a928df · utf8-tests-green-2026-07-17 unicode/utf8 matches go test, test for test.
2026-08-08 Go programs run on Linux linux-first-run-2026-08-08 Converted programs, the fatih/color walkthrough included, match go run on Linux.
2026-10-01 Every implementable standard-library package validates 133ca704e · nuget-1.24.13.3 Every implementable package passes its own Go tests in C#, runtime included, on Windows and Linux.

C# to Go?

A full code-based conversion from C# to Go is not offered (it would require so many restrictions as to be impractical). To call compiled .NET code from Go instead, see go-dotnet (CLR hosting for .NET Core) or embedding Mono via cgo for traditional .NET.

License

go2cs contains components under several licenses. The converter (src/go2cs) is licensed under AGPL-3.0-only with the go2cs Converter Output Exception, an additional permission under AGPL section 7. Alternative commercial licensing for the converter is available from the copyright holder listed in AUTHORS, at the contact address given there. Original runtime/support libraries and Roslyn generators use MIT. Standard-library packages, including project-owned handwritten additions, use BSD-3-Clause, with upstream notices preserved.

Code generated by go2cs is not subject to the converter’s AGPL license merely because it was produced by go2cs, and the output exception says so as a binding term: rights in output remain governed by the input source and any separately licensed components incorporated into or referenced by the output, and the project scaffolding the converter emits into your output is MIT. Running the converter, locally or in your own build, triggers no AGPL obligation; only distributing the converter, or offering a modified converter to others over a network, does.

Commercial licensing, sponsorship and acquisition. go2cs is developed by a single maintainer and will stay free for its users. A company that wants to ship the converter inside a proprietary product, offer it as a service without publishing its changes, or take on the project’s long-term development is welcome to talk: a commercial license, sponsorship of ongoing work, or an outright acquisition that keeps the tool free for the community are all on the table. The contact address is in AUTHORS. Contributions to the converter carry a Developer Certificate of Origin sign-off and a relicensing grant, see CONTRIBUTING.md, which is what keeps those options open. The go2cs name and logo identify this project; a fork is welcome under the AGPL but should carry its own name.

See LICENSING.md for the component matrix, exceptions and output policy, and NOTICE for attribution.