Go Beyond .NET: Your Step-by-Step Journey from C#/.NET to Go - Part 5
Dan Liviu Nicoara
Senior Go Backend Developer | Software Engineer | Contractor | Freelancer
Hey there, fellow developers! ??
Welcome back to our journey of transitioning from .NET to Go. We've covered a lot so far, and today we're diving into the fascinating world of interfaces and classes in both .NET and Golang. Buckle up, it's about to get interesting!
??Part 1: Interfaces
What is an interface? An interface is defined as an abstract type used to specify behavior. Think of it as a blueprint for classes.
Why use an interface? Interfaces are useful for a number of reasons. They allow for greater abstraction, making it easier to write flexible and reusable code. They also make it possible for objects of different types to interact with each other, as long as they implement the same interface. This is known as polymorphism.
Other reasons:
?? Interfaces in C#/.NET
Interfaces in .NET are like contracts defining a set of methods or properties a class must implement. They provide a way to achieve abstraction, allowing you to define the structure of a class without specifying how it should be implemented.
Interfaces are declared using the interface keyword
in C#, it is considered good practice to start with the letter "I" at the beginning of an interface, as it makes it easier for yourself and others to remember that it is an interface and not a class.
By default, members of an interface are abstract and public.
Note: Interfaces can contain properties and methods, but not fields.
Starting from C# 8, interfaces can have default implementations for methods.
Interfaces can inherit from one or more interfaces.
Starting from C# 9, you can use the sealed modifier on interfaces to prevent further interface inheritance.
Use Cases:
?? Interfaces in Go
An interface type in Go is kind of like a definition. It defines and describes the exact methods that some other type must have.
You declare an interface using the type keyword followed by the interface keyword and a set of method signatures.
Interfaces enable polymorphic behavior in Go, allowing different types to be used interchangeably if they implement the same interface.
Interfaces can be composed of multiple other interfaces.
Empty interface
Unlike C#, Go has the concept of an empty interface, meaning the method set of an interface doesn’t have to contain at least one member. It can be completely empty.
An empty interface (interface{}) has zero methods and can hold values of any type. It's used in situations where you need to work with values of unknown types.
Prominent use of empty interface exists in variadic or generic functions:
In this example, the printValue function takes an empty interface as a parameter. This allows it to accept values of any type. The main function demonstrates calling printValue with different types, including an integer, a string, a float, a slice of integers, and a custom Person struct. The empty interface makes it possible to work with a variety of types without specifying them explicitly in the function signature.
Interfaces are widely used in Go for dependency injection, decoupling components, and achieving a high level of abstraction in code.
??Part 2: Classes
A class is a group of related methods and properties. A class describes these things, and in most cases, you create an instance of this class, now referred to as an object. On this object, you use the defined methods and properties. Of course, you can create as many instances of your class as you want to. Classes, and object-oriented programming in general, is a huge topic. We will cover some of it in this article but not all of it.
?? Classes in C#/.NET
In C#, a class defines a data structure (attributes/fields) and behaviors (methods) that the objects created from the class will have. A class is declared using the class keyword.
Fields are variables that store data within a class. Properties provide a way to get and set the values of private fields.
Constructors are special methods called when an object is created. They initialize the object's state.
Methods are functions defined within a class. They define the behavior of objects created from the class.
Classes can inherit from other classes, forming a hierarchy. In C#, you use : to indicate inheritance.
Polymorphism allows a derived class to use the functionalities of its base class. It includes method overriding and interface implementation.
Access modifiers (public, private, protected, internal, etc.) control the visibility of class members. They determine which parts of the program can access the members.
A class can implement one or more interfaces (separated by commas) using the class : interface syntax.
?? Classes in Go
In Go, unlike C#, there is no explicit "class" keyword, but instead, we have the struct{} and method receiver concepts.
Before exploring the implementation of interfaces, let's understand these 2 concepts.
Structs
A struct is a composite data type in Go that groups together variables (fields) under a single name. Fields within a struct{} can be of different data types.
You declare a struct using the type keyword followed by the struct keyword and a list of field declarations.
You can create instances of a struct by providing values for its fields during initialization. You access fields of a struct using the dot (.) notation.
You can create functions that act as constructors to initialize and return an instance of a struct.
Go supports composition by embedding one struct into another, similar to class inheritance in other languages.
Here's an example of composition in Go:
Method Receivers
In Go, methods are functions associated with a specific type. A method receiver is a parameter on a method that defines which type the method operates on.
The syntax for a method receiver looks like this:
Pointer Receivers:
Using a pointer as a receiver (func (p *Person) methodName()) allows the method to modify the actual value of the instance. This is useful when you want to modify the state of the struct.
Value Receivers:
Using a value as a receiver (func (p Person) methodName()) operates on a copy of the struct. This is useful when you don't need to modify the state of the struct and want to work with a copy.
Go does not have the concept of access modifiers (like public, private, or protected) as seen in C#. Instead, Go follows a simple and explicit approach to access control:
Okay, now let's get back to implementing an interface.
Go interfaces use "duck typing". Duck typing determines a "match" by looking at the behavior of the thing:
??If it walks like a duck and quacks like a duck, then it must be a duck.
Go determines that a struct "implements an interface" when the necessary function signatures exist on that struct.
We say that something satisfies this interface (or implements this interface) if it has a method with the exact signature( e.g Log(string)).
In this example:
This allows you to use either a FileLogger or a ConsoleLogger interchangeably wherever a Logger is expected, demonstrating the flexibility and power of interfaces in Go.
?? What happens if the struct doesn't implement all methods of interface?
When a struct implements an interface, it should provide an implementation for all the methods of the interface. If it fails to implement any method, we will get an error.
?? Abstraction behaviors
Interface type value gives access ONLY to methods of its interface type. It hides all details about the exact value like if it’s struct.
??Conclusion
As we wrap up our exploration of interfaces and classes, let's do a quick side-by-side comparison of these two diverse languages.
.NET:
Golang:
In transitioning, remember it's not about choosing between good and bad; it's about adapting to the right tool for the job.
Have questions or specific topics you'd like to explore in this series? Feel free to drop them in the comments, and I'll be happy to address them in upcoming articles.
Happy coding, and stay tuned for more! ?? #DotNET #Golang #Coding