DEV Community

syauqi fuadi
syauqi fuadi

Posted on

From C# to Go: Simplifying Design for Object-Oriented Developers

As a C# developer, you’re accustomed to the robust features of an object-oriented language: classes, inheritance, interfaces, and generics. When transitioning to Go (Golang), however, you’ll encounter a language that prioritizes simplicity and pragmatism. Let’s explore how Go’s design philosophy challenges traditional OOP concepts—and how to adapt effectively.


1. Structs Instead of Classes: A Lightweight Alternative

In C#, classes encapsulate data and behavior with properties, methods, and constructors. Go replaces classes with structs, which are simpler data containers. Methods are defined separately and attached explicitly to struct types.

// C# Class  
public class Product {  
    public string Name { get; set; }  
    public void Display() => Console.WriteLine(Name);  
}  
Enter fullscreen mode Exit fullscreen mode
// Go Struct  
type Product struct {  
    Name string  
}  

// Method attached to the Product struct  
func (p Product) Display() {  
    fmt.Println(p.Name)  
}  
Enter fullscreen mode Exit fullscreen mode

Go’s approach decouples data from behavior, encouraging flexibility without the overhead of traditional class hierarchies.


2. Composition Over Inheritance

C# developers often rely on inheritance to share code between related types. Go, however, omits inheritance entirely. Instead, it promotes composition through struct embedding.

// C# Inheritance  
public class Vehicle {  
    public int Wheels { get; set; }  
}  

public class Truck : Vehicle {  
    public void LoadCargo() => Console.WriteLine("Loading...");  
}  
Enter fullscreen mode Exit fullscreen mode
// Go Composition  
type Vehicle struct {  
    Wheels int  
}  

type Truck struct {  
    Vehicle // Embedded struct  
}  

func (t Truck) LoadCargo() {  
    fmt.Println("Loading...")  
}  

// Usage: myTruck := Truck{Vehicle{Wheels: 6}}  
// myTruck.Wheels and myTruck.LoadCargo() are both accessible  
Enter fullscreen mode Exit fullscreen mode

By embedding structs, Go achieves code reuse without the complexity of inheritance chains.


3. Interfaces: Implicit and Flexible

In C#, interfaces require explicit implementation. Go takes a different approach: interfaces are satisfied implicitly if a type has the required methods. This reduces boilerplate and encourages decoupled design.

// C# Explicit Interface  
public interface ILogger {  
    void Log(string message);  
}  

public class FileLogger : ILogger {  
    public void Log(string message) => // Write to file  
}  
Enter fullscreen mode Exit fullscreen mode
// Go Implicit Interface  
type Logger interface {  
    Log(message string)  
}  

type FileLogger struct{}  

func (f FileLogger) Log(message string) {  
    // Write to file  
}  

// FileLogger automatically satisfies the Logger interface  
Enter fullscreen mode Exit fullscreen mode

This implicit approach allows types to evolve independently, fostering modular architecture.


4. Generics: Purposeful and Restrained

C# developers leverage generics extensively for reusable, type-safe code. Go introduced generics in version 1.18, but they’re designed to be used judiciously:

// Go Generics Example  
func PrintSlice[T any](slice []T) {  
    for _, item := range slice {  
        fmt.Println(item)  
    }  
}  
Enter fullscreen mode Exit fullscreen mode

While less versatile than C# generics, Go’s implementation prioritizes readability and avoids overcomplication.


5. The Value of Minimalism

Go’s design encourages straightforward solutions. Without traditional OOP constructs like inheritance or abstract classes, developers are guided toward simple patterns:

  • Composition for code reuse.
  • Interfaces for polymorphism.
  • Functions as first-class citizens.

This minimalism reduces cognitive overhead, making it easier to write and maintain scalable systems.


Conclusion: When to Choose Go

Go isn’t a replacement for C# in every scenario. Complex enterprise applications may still benefit from C#’s rich OOP features. However, Go excels in:

  • Microservices (with built-in concurrency support).
  • CLI tools (fast compilation and single binaries).
  • High-performance systems (efficient resource usage).

For developers seeking a streamlined approach to modern problems, Go offers a compelling blend of simplicity and power.

Up next: A closer look at Go’s error-handling model versus C# exceptions.

Top comments (4)

Collapse
 
chand1012 profile image
Chandler

Great article! This was especially interesting to me since I actually made the opposite transition, starting out as a Go developer then moving to C# for a few Unity project. I wrote a few similar articles on Go, one for high level devs coming over to a statically type language (Go being what I was learning at the time) and a high level guide on how OOP paradigms work in Go.

Collapse
 
syawqy profile image
syauqi fuadi

wow, great insight from your side too, thanks for sharing

Collapse
 
syawqy profile image
syauqi fuadi

thanks for this, it sure help me and others who sometimes forget about this