Dotnet logo

.NET Tools

Essential productivity kit for .NET and game developers

How-To's

Easy conversions and readability – code smells series

This post is part of a 10-week series by Dino Esposito (@despos) around a common theme: code smells and code structure.

Welcome back to our series about code smells! Today, we’ll look at overriding the ToString method and making use of extension methods.

In this series:

In the .NET Framework, getting the text description of a class instance is as easy as calling the ToString method – one of the foundational methods of the root System.Object class. In its default implementation, the method simply returns the type name.

Overriding ToString() for readability

A plain override can instruct the class to return a more meaningful string!

The code caught in action uses a Booking class with a child property of type TimeInterval that doesn’t override its ToString method. The TimeInterval class is made of two DateTime properties named From and To. Now imagine the TimeInterval class would convey its state to text as below.

public override string ToString()
{
    const string text = "Unlimited";

    if (From.IsMin() && !To.IsMax())
        return $"up to {To.ToShortDateString()}";

    if (!From.IsMin() && To.IsMax())
        return $"till {From.ToShortDateString()}";

    if (!From.IsMin() && !To.IsMax())
        return $"{From.ToShortDateString()} - {To.ToShortDateString()}";

    return text;
}

The developer experience is quite different now:

Better string representation of an object by overriding ToString

The output of the ToString method is a bit friendlier: “till 1/1/2018”. Granted, for the sake of debugging, you could use the DebuggerDisplayAttribute to achieve this as well. But overriding the ToString method helps in our code base as well: whenever we implicitly or explicitly cast a TimeInterval instance into a string, for example when logging its value, you get meaningful output instead of just the type name.

This is only a first basic example of code readability. In the code snippet above that shows the implementation of ToString, you may have noticed two weird functions named IsMin and IsMax.

Those functions are called from within a DateTime object, but no version of the .NET Framework extends the base DateTime type with such methods. They are, in fact, two extensions methods.

public static class DateExtensions
{
    public static bool IsMin(this DateTime theDate)
    {
        return theDate == DateTime.MinValue;
    }

    public static bool IsMax(this DateTime theDate)
    {
        return theDate == DateTime.MaxValue;
    }
}

In C#, extension methods are ultimately static methods that undergo some compile tricks to turn them into tasteful syntactic sugar for developers. The net effect, however, is that developers can extend system classes (and in general: any class they don’t own) with external methods that make class instances easier to read and understand.

In the example here, IsMin and IsMax are a replacement for an if statement. In some other cases, the replacement can compress a few lines of noisy code into a single and self-explained method call. This is just the case with numeric conversions.

Turning a numeric string like "1" into the corresponding number has never been a quick task in the .NET Framework and related languages. Here’s what it takes:

if (int.TryParse(theString, out int temp))
{
    // some work here
}

You have the input string, and you have a temporary variable that ends up containing an integer. You can implement whatever subsequent logic you need. The problem is not much what you can do but how you actually do it.

Here’s another approach that uses a ToInt extension method.

public static int ToInt(this string theString, int defaultValue = 0)
{
    return int.TryParse(theString, out var temp)
        ? temp
        : defaultValue;
}

As you can see, the actual parsing code is the same but it’s now wrapped up into a method that extends the string type. Try running the code below and see what happens.

var actualNumber1 = "123".ToInt();
Console.WriteLine($"String '123' converts to number: {actualNumber1}");

var actualNumber2 = "hello".ToInt(-1);
Console.WriteLine($"String 'hello' converts to number: {actualNumber2}");

Using extension methods to make code more readable

The ToInt method works just as expected but it gives a lot more flexibility to express your programming will. In particular, you can directly specify the value to return if the conversion fails. And if you just need to know if the string can be turned into a number, you’re only a small exercise away from it.

Next week, we will look at bringing code readability closer to the language of the business domain.

Download ReSharper 2018.1.2 or Rider 2018.1.2 and give them a try. They can help spot and fix common code smells! Check our code analysis series for more tips and tricks on automatic code inspection with ReSharper and Rider.

image description