DEV Community

Cover image for 🕰️My C# Time Machine: 13 Lessons From a 10-Year Voyage
ByteHide
ByteHide

Posted on • Originally published at bytehide.com

🕰️My C# Time Machine: 13 Lessons From a 10-Year Voyage

Reflecting on my decade-long journey with C#, I find myself back at the beginning. A time when each line of code was an experiment, each bug a stumbling block on the path to progress. Solutions danced just out of reach, teasingly close yet confoundingly elusive. But every hurdle surmounted, every victorious moment when code ran flawlessly, brought with it a sense of accomplishment that made every setback worthwhile.

In hindsight, the wisdom cultivated through evenings hovered over my keyboard becomes glaringly apparent. I find myself craving a path back to the past, an opportunity to guide my younger self. To unravel the intricacies of logic in C#, to master the art of debugging, to explain the principles of object-oriented programming.

How I wish I could illuminate this path of discovery, soothing the sting of trial by fire. Fast forward through hours of relentless debugging and lead my past self swiftly towards the realm of C# proficiency.

If you are setting out on your journey with C#, or find yourself in the labyrinth of code somewhere along the way, the lessons I’ve learned may serve as your guiding light. I offer you these 13 pearls, insights skimmed from a decadal voyage across the ocean of C#. Embrace these lessons as a compass, pointing you towards a code-laden horizon.

Embark on this journey as we delve into “My C# Time Machine: 13 Lessons From a 10-Year Voyage,” offering you a collection of lessons I wish I’d had at the start of my C# exploration.

Part I: OOP, Handle Errors, Tooling

In the initial days of my exploration with code, the words I would whisper to my past self in those still, early mornings would be:

“OOP- Encounter It, Understand It, Use It!”

Why, you might ask?

Impressively complex and incredibly error-prone coding constructs were the result of my early inability to grasp Object-Oriented Programming (OOP) fully. I remember building a class, “EtchASketch,” with a bunch of commands and functions. But fundamental concepts like inheritance, polymorphism, abstraction, and encapsulation were like closed books to my past self.

Once I built a good understanding of these pillars of OOP, my code became efficient, the complex class transformed into smaller, logical parts interacting with each other seamlessly.

In the world of coding, you must prepare for unexpected situations; harsh yet crucial learnings came from my experiences with error handling. I remember clearly, during a crucial demonstration, our application crashed due to an unhandled exception caused by a NULL reference.

This was a painful lesson about always expecting the unexpected with code, as the snippet below demonstrates, where I try to divide by zero. Using appropriate error handling methods such as the try-catch block could have avoided those early mishaps:

try
{
   int x = 0;
   int y = 5 / x;
}
catch (DivideByZeroException e)
{
   Console.WriteLine("An error occurred: " + e.Message);
}
Enter fullscreen mode Exit fullscreen mode

On a third note, it’s essential to use the right tools. So many of my early challenges could have been tamed more easily with the correct toolset at hand.

“Explore Visual Studio. Discover the power of JetBrains Rider. These are your main gears in this technical journey!”

Debugging features, hot reload, file organization – these integrated development environments had everything I needed. Ignoring them early in my career cost me time and effort, a mistake rectified as I became more comfortable with the tools of the trade.

Part II: LINQ, ASP.NET Core, Delegates

Drifting into our fourth chapter, we find ourselves inside the elegant dance of the Language Integrated Query – or as we coders know it, LINQ. Describing it wouldn’t be enough, allow me to share a simple code snippet to illustrate its simplicity and power.

Before LINQ, you would write a foreach loop to find all odd numbers in a list:

List<int> numbers = new List<int>() { 1, 2, 3, 4, 5 };
List<int> oddNumbers = new List<int>();

foreach (var num in numbers)
{
    if (num % 2 != 0)
    {
        oddNumbers.Add(num);
    }
}
Enter fullscreen mode Exit fullscreen mode

In the world of LINQ, this operation gets simpler:

List<int> numbers = new List<int>() { 1, 2, 3, 4, 5 };
List<int> oddNumbers = numbers.Where(n => n%2 != 0).ToList();
Enter fullscreen mode Exit fullscreen mode

LINQ translates raw SQL-like queries into the native language of C#. It simplifies complex operations and brings forth a more readable and maintainable code experience.

Venturing into the fifth revelation, we encounter the mighty ASP.NET Core. An important tool in any C# developer’s arsenal, especially when interacting with web development, let’s take a look at the “Hello, World!” example:

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

public class Startup
{
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseRouting();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapGet("/", async context =>
            {
                await context.Response.WriteAsync("Hello, World!");
            });
        });
    }
}
Enter fullscreen mode Exit fullscreen mode

ASP.NET Core simplifies web application setup, effortlessly handling the server and routing for us. It shifts our focus from the repetitive boilerplate configurations to solving real problems that address user needs.

Our narrative concludes with the sixth revelation – the power of Delegates and Events. Invoking a delegate allows you to create a customizable method that can be passed as a parameter. Here’s a basic demonstration:

// A delegate type for encapsulating a method that takes an int and returns void:
public delegate void IntMethod(int i);

public class MyClass
{
    private IntMethod myDelegate;

    public MyClass(IntMethod passedDelegate)
    {
        myDelegate = passedDelegate;
    }

    public void InvokeDelegate(int i)
    {
        myDelegate(i);
    }
}
Enter fullscreen mode Exit fullscreen mode

In this minimal example, the delegate IntMethod is defined to encapsulate a method that takes an integer parameter and returns void. Using delegates and events adds a new level of dynamism to your C# program and significantly improves the structure of your code.

Part III: NuGet, Code Readability, NUnit

Seventh on my list: the Package Savior, NuGet. I wrangled with dependencies, wrestled with DLLs vying for dominance, battling for the peace of compile-time. Then NuGet appeared, the unsung hero, the gentle giant of package management. It came bearing gifts, offering easier dependency management across projects, making what was once a difficult trek a mere stroll in the park.

// Before NuGet, manually handling dependencies
Copy DLLs > Add reference > Repeat for other projects

// After NuGet, simplified dependency management
Install-Package ExamplePackage
Enter fullscreen mode Exit fullscreen mode

Lesson eight, a simple one, yet it had a profound impact on my codesmithing: the art of crafting readable code. Sometimes, the most complex solution isn’t the best; sometimes it’s the most simple one. Deciphering old, cryptic code is like reaching back through time and questioning your past judgements. You’ll thank yourself for the present of readable, maintainable code.

// Cryptic code
DoThing(42);

// Clear, readable code
CalculateAgeOfUniverseInYears(42);
Enter fullscreen mode Exit fullscreen mode

The ninth revelation hails from the realm of unit testing – NUnit. Boldly I wrote code, untested and raw, only to watch it spectacularly implode. The idea of “write, test, fix, repeat” quickly transformed from a playful game to an essential ritual. NUnit became the gatekeeper, ensuring each line of code performed its destined task without disturbing the peaceful harmony of the overall system.

// Without NUnit, code testing is haphazard
public void WriteCode()
{
    Code code = new Code();
    // Write code
    code.Write();
    // Hope and pray it works
}

// With NUnit, code testing is organized and effective
[Test]
public void WriteCodeTest()
{
    Code code = new Code();
    // Write code
    code.Write();
    // Test with NUnit assert function
    Assert.IsTrue(code.works);
Enter fullscreen mode Exit fullscreen mode

Stepping into double digits, the tenth item on my list is ConfigureAwait. I should have paid more attention to Async/Await. I was young, ambitious and– as it turns out– terribly naive. I charged headfirst into programming tasks concurrently, imagining immediate rewards and increased efficiency. Instead, the overlooking of ConfigureAwait left applications hanging and threads starving.

// Without ConfigureAwait
public async Task HeavyTaskAsync()
{
    await HeavyProcess();
    ContinueAfterHeavyProcess();
}

// With ConfigureAwait
public async Task HeavyTaskAsync()
{
    await HeavyProcess().ConfigureAwait(false);
    ContinueAfterHeavyProcess();
}
Enter fullscreen mode Exit fullscreen mode

Eleventh in line shines a light outside of the C#. The more languages you know, the better you become in each. A programmer spreading the net wider learns about different voices, approaches, and patterns. It’s like stepping out of your neighbourhood– everything you discover, you bring back home and apply to your own work.

Twelfth, the principle of simplicity as a beacon of brilliance. Learn to dispose of unwanted code, the rambles that clutter your digital realm. Don’t add new floors on a shaky old foundation– start afresh with sturdier materials.

Thirteenth, isn’t a lesson but an insight. Coding is a lifelong journey, not a destination. Each bug approached with a fresh perspective, each day an opportunity to acquire, apply and diversify knowledge. Coding isn’t an art; it’s a living, breathing way of life.

They say you truly become a coder when you spend more time reading and deciphering code than writing it. Yes, code-readability is paramount.

As I find myself nearing the end of this introspective journey, a decade-long liaison with C#, I realise coding isn’t just sequences of logical operations. It flowers from a seed of an idea, mutating through iterations, graced by failures and triumphs. It’s a constant learning, a dynamic evolution that transforms not just the creator, but the creation too. And so, we code, in pursuit of that zenith of perfection, armed with the knowledge that the journey is the destination. So, keep coding, keep learning, and most importantly, keep growing.

Top comments (2)

Collapse
 
sack251 profile image
sack251

That's crazy man, thank you very much for this

Collapse
 
sburchfield profile image
Spencer

This was very helpful. I have dabbled a tiny bit in C# but I have 15 credit hours of C# classes coming up. This gave me a clean slate on how I should go about things. Thank you!