Skip to main content
Background Image

Learning Go as a .NET Developer

·1479 words·7 mins·

I’ve been a .NET developer for 3 years now. Throughout my journey I’ve learned so much, and at the same time, I’ve had a lot more questions. The .NET ecosystem is big and mature, but the learning curve is also noticeable. From the C# language itself to the ASP.NET Core framework and all the tools one needs to know like Entity Framework. Sometimes I wondered if other languages have this much to offer as well. I’ve always been a practical hands-on type of person. I like to know how things work, and how I can make them better. One of my favorite things to do is having technical discussions with my developer friends. Talking about how things work, interesting challenges we’ve faced, fun things we’ve done, etc. One of them is a Go developer. Before talking to him, I knew of Go, but I had no idea what it looked like. But very quickly it got my attention. I liked the core philosophy of the language, Simplicity. The Go programming language aims to keep things simple all the while giving you the power to do anything you want. Anything you can do in C#, you can do in Go.

You can check the language out here!

I studied The Go Programming Language book. It’s an amazing starter book. you can find it here!

Memory Management & Garbage Collection
#

While learning about memory management in Go, I realized how similar it is to C#. Both languages use a garbage collector. C# has reference types and Go has safe pointers. At first glance they looked and felt like each other. Both reference types and safe pointers aim to cover the un-safety of pointers. They actually behave quite similar. In Go whenever you want to pass a reference you simply pass a pointer to the variable, and the pointers can be nil, Go’s null construct.

// C#
SomeMethod(person) // classes are reference types by default
// Go
someMethod(&person) // looks like c-based pointers, but it's safe!

Functions
#

Go functions can return multiple values by default. In C# it’s kind of similar to using Tuples but it’s not exactly the same, but looks like it. Since it’s part of Go it allows the developers to use “The Result Pattern”. We’ll talk about this more in “Error Handling”.

// C#
// using Tuples to return multiple values
public (int v1, string v2) SomeMethod() {} 
// Go
// returning multiple values
SomeMethod() (v1 int, v2 string) {}

In Go, the returned types come after the parameter list of the function, unlike C#. You might have also noticed that the type of the variables come after their identifier in Go!

The Compiler
#

The compiler is where Go shines more than C# in my opinion. The compiler acts as a major safeguard here. A lot of the bugs show up at compile-time. The language doesn’t have a JIT compiler unlike C#. Go uses the compiler to enforce the syntax in a stricter manner. For example, your variables must be used otherwise that’s a compiler error. This helps a lot with removing unnecessary memory allocation. Returned variables from functions must be used or explicitly discarded.

// Go
SomeMethod() (v1 int, ok bool) {}

SomeMethod() // compiler error! returned values not used!

I feel safer and more comfortable with Go’s compiler.

Concurrency
#

C#’s concurrency model, is vast. You have multiple ways to make something concurrent. each one has it’s own challenges and merits. More importantly it requires you to know it well before using it. While it’s a powerful system, it can be daunting to start with. It took me some time to get used to and learn beyond async/await.

One of the very first things that caught my eye when I started learning GO was it’s concurrency system. Go has a CPS-Style (communicating sequential processes) concurrency model. What does that mean? Wikipedia says:

communicating sequential processes is a formal language for describing patterns of interaction in concurrent systems.

This short article is useful as well if you are interested.

You can learn Go’s concurrency patterns effectively using Concurrency.Rocks! Due to how big this topic is, I’ll link to the website rather than showing code here, It’s a much better learning material for this topic!

Goroutines
#

Goroutines are lightweight concurrency constructs which allow one to run functions in the background. The syntax is straightforward, any function you want to run in the background, simply put the go keyword behind its call. No more async/await, no more Task or Thread. It simply does what it should. You don’t need to worry about threadpools, creating manual threads, using TaskCompletionSource, none of that. A notable thing about goroutines is that you can’t have them return something. To get a value from a goroutine, you have to use Channels, another interesting GO construct.

Channels
#

Channels are something that you’ll face early when learning the language. Channels are another concurrency construct. They allow use to safely pass data in our program in a thread-safe manner. Think of channels as an in-memory message queue. While there’s more to it, this isn’t a Go tutorial so I won’t be showing too much syntax.

Error Handling
#

Handling errors is also one of the thing’s where the two languages differ from each other significantly. C# and other C-Based languages mainly use exceptions to handle error. There’s been a lot of discussions about the performance of exceptions in recent years. Go uses something called the “Result Pattern” in the world of software. What that means is that it basically returns whether the function resulted in an error or not. Though this isn’t a syntax only for errors. In the Functions section we talked about returning multiple values from functions. Well, by convention, there are two types of fault handling we can have:

  • Returning a bool value named ok as the last returned value SomeMethod() (v1 int, ok bool)
  • Returning an error which is a struct in the standard go package SomeMethod() (v1 int, e error)

Both of these methods are used regularly in Go programs. This reduces a lot of the performance problems with exceptions while making sure one point stands throughout your program:

A good program knows whether it’ll return an error or not.

Exceptions are unknown. Specially when you have a global catch(Exception e). A good program should know what types of errors, if any it’ll return and handle it gracefully, Go emphasizes on this.

Why Go?
#

Now you might think, where would I use Go? For me, .NET is still the language to write my Apis in. The ecosystem is just too mature, and having put the time in to learn it, it’s actually quite enjoyable to develop applications in .NET. Each language is a tool and each one has strengths in it’s own place. Go’s simple yet effective concurrency model makes it the perfect tool to handle a lot of calls for simple methods, while C# is more focused on delivering business quality allowing one to develop complex businesses. I don’t like the feeling of being too dependent on one language.

Go is a Modern Language
#

It was designed at Google in 2007 by Robert Griesemer, Rob Pike, and Ken Thompson, and publicly announced in November of 2009. - Wikipedia

Go learned a lot from it’s predecessors. It aims to please with it’s powerful simplicity. One thing which caught my attention about the language was that it felt “low-level” while still being a “high-level” language. But maybe that’s just me comparing the syntax to C#.

Not having a Just-In-Time compiler also means it’s much easier to build your program for different OS, Although not that it matters anymore since .NET has become cross-platform recently. But that still makes the executables more lightweight and easier to port. I would actually prefer writing system programs, cli tools and other similar type of programs in go instead of .NET, it just feels right with Go.

Another way learning Go has helped me, was making me deepen my knowledge in C#. When studying the language, I often questioned myself on how I would achieve the same result of a program in each language. One example was Channels. I quickly realized that C# also has channels and that it was added in .NET Core. I remember how happy and excited I was! By learning how two or more languages approach the same challenges and problems, one can develop better software knowing what happens underneath all the cute interfaces and Apis. Languages and frameworks are tools one learns to apply. We often forget why we learn programming. To solve problems. Just because you know one language doesn’t mean you can’t learn another. Software development is all about depth and perspective!

I hope this gave you some insight to the Go Programming language and maybe it made you want to try it out!

Mazdak Parnian
Author
Mazdak Parnian
Software Engineer using .NET & GO