C#
GO

C# to Go

10 lessons

Progress0%
1Introduction2Type System3Classes to Structs4Interfaces5Error Handling6Async to Goroutines7Generics8LINQ to Slices9Testing10Packages and Modules
All Mirror Courses
C#
GO
Packages and Modules
MirrorLesson 10 of 10
Lesson 10

Packages and Modules

Go modules and packages vs C# namespaces and NuGet

Introduction

In this lesson, you'll learn about packages and modules in Go. Coming from C#, you already have a foundation for understanding this concept. We'll build on that knowledge while highlighting the key differences.

Mirror Card
C#
From C#:

In C#, you're familiar with go modules and packages vs c# namespaces and nuget.

GO
In Go:

Go has its own approach to go modules and packages vs c# namespaces and nuget, which we'll explore step by step.

The Go Way

Let's see how Go handles this concept. Here's a typical example:

GO
Go Example
// Every .go file declares its package
package math  // directory name convention

// ONLY visibility: Uppercase = exported, lowercase = unexported
func Add(a, b int) int { return a + b }    // exported (public)
func helper() {}                            // unexported (internal)

// init() — auto-runs before main (like C# static constructor)
func init() { /* one-time package setup */ }

// go.mod — dependency manifest
// module github.com/user/myapp
// go 1.21
// require github.com/gin-gonic/gin v1.9.1

// Import
import (
    "fmt"                          // stdlib
    "github.com/user/myapp/math"   // local package
    "github.com/gin-gonic/gin"     // external
)

// Commands
// go mod init github.com/user/myapp  (like dotnet new)
// go get github.com/gin-gonic/gin    (like dotnet add package)
// go mod tidy                         (remove unused)
// go build -o app .                   (single binary — no runtime needed)
// GOOS=linux go build -o app-linux .  (cross-compile)

Comparing to C#

Here's how you might have written similar code in C#:

C#
C# (What you know)
// Namespace (like Go package)
namespace MyApp.Math;

// Access modifiers
public class Calculator { }       // accessible everywhere
internal class Helper { }         // assembly-private
private class Nested { }          // class-private

// File-scoped namespace (C# 10)
namespace MyApp.Utils;
public static class StringUtils { }

// Global using (C# 10)
global using System.Collections.Generic;

// .csproj — dependency management
// <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
// dotnet add package Newtonsoft.Json

// NuGet commands
// dotnet add package Serilog
// dotnet restore
// dotnet build
// dotnet run
// dotnet publish -c Release -r linux-x64 --self-contained
Mirror Card
C#
From C#:

You may be used to different syntax or behavior.

GO
In Go:

Uppercase = exported (public); lowercase = unexported — no public/internal/private keywords

Mirror Card
C#
From C#:

You may be used to different syntax or behavior.

GO
In Go:

go.mod is the C# .csproj equivalent; go.sum is the lockfile

Mirror Card
C#
From C#:

You may be used to different syntax or behavior.

GO
In Go:

Go compiles to a single static binary — no .NET runtime needed on target machine

Mirror Card
C#
From C#:

You may be used to different syntax or behavior.

GO
In Go:

Cross-compilation: GOOS=linux GOARCH=amd64 go build — no equivalent dotnet command

Mirror Card
C#
From C#:

You may be used to different syntax or behavior.

GO
In Go:

Package path = module path + directory, not C#'s namespace which is arbitrary

Step-by-Step Breakdown

1. Export by Capitalization

In Go, there are no access modifiers. Capitalize to export — applies to functions, types, constants, struct fields.

C#
C#
public class Calc {
    public int Add(int a, int b) => a + b;
    private int Helper() => 0;
}
GO
Go
package calc

func Add(a, b int) int { return a + b } // exported
func helper() int { return 0 }          // unexported
Rule of Thumb
Capitalize to export — this applies to functions, types, constants, struct fields, everything.

2. go.mod vs .csproj

go.mod declares the module path (used in import paths) and dependencies with pinned versions. go.sum is the lockfile.

C#
C#
<PackageReference Include="Serilog" Version="3.1.1" />
dotnet add package Serilog
GO
Go
// go.mod
require github.com/rs/zerolog v1.31.0

// Commands
go get github.com/rs/zerolog@latest
go mod tidy  // remove unused
cat go.sum   // lockfile — commit it

3. Single Binary Deployment

go build produces a self-contained binary with no runtime dependency. Cross-compilation is built-in with GOOS/GOARCH env vars.

C#
C#
dotnet publish -c Release -r linux-x64 --self-contained
GO
Go
# Build for current OS
go build -o app .

# Cross-compile for Linux
GOOS=linux GOARCH=amd64 go build -o app-linux .

# Cross-compile for Windows
GOOS=windows go build -o app.exe .

4. internal Package

Go's 'internal' directory restricts packages to the parent module only — equivalent to C# internal access modifier.

C#
C#
// C# internal class
internal class Helper { }
GO
Go
// app/internal/db/db.go
package db
// Can only be imported by code in app/ or its subdirectories
// Prevents external packages from using app/internal/db

Common Mistakes

When coming from C#, developers often make these mistakes:

  • Uppercase = exported (public); lowercase = unexported — no public/internal/private keywords
  • go.mod is the C# .csproj equivalent; go.sum is the lockfile
  • Go compiles to a single static binary — no .NET runtime needed on target machine
Common Pitfall
Don't assume Go works exactly like C#. While the concepts may be similar, the syntax and behavior can differ significantly.

Key Takeaways

  • Uppercase exports, lowercase doesn't — no access modifier keywords
  • go.mod = .csproj; go get = dotnet add package; go mod tidy = remove unused
  • Single static binary — no .NET runtime needed; built-in cross-compilation with GOOS/GOARCH
  • internal/ directory restricts package access — Go's equivalent of C# internal access
Rule of Thumb
The best way to learn is by doing. Try rewriting some of your C# code in Go to practice these concepts.
PreviousFinish