ARTICLE

Making Types Work for You

This article discusses the importance of using data types in programming for writing and maintaining code bases.

Types

Programmers take data types for granted. Some even argue that programmers are faster in dynamically typed languages like JavaScript or Python because they don’t have to deal with intricate details like deciding the type of each variable.

Being strong on the type

I’m sure most programming languages have types. Even the simplest programming languages like BASIC had types: strings and integers; some of its dialects even had real numbers. Types were invented to make our lives easier. Types are free checks for correctness, and understanding the underlying type system can help tremendously to make you a productive programmer.

  • Compiled programming languages tend to be stricter. How strict they are depend on how much pain the language designer wants to inflict upon you. For example, Rust language can be considered the German-engineering of programming languages, extremely strict, perfectionist, and therefore error-free. C language can also be considered German-engineering but like Volkswagen: it lets you to break the rules and pay the price later. Both languages are statically typed, once a variable is declared its type can’t change, but Rust is called strongly typed like C# but C is considered weakly typed.
Table 1. Flavors of type strictness in programming languages

Proof of validity

Proof of validity is one of the less-known benefits of having predefined types. Suppose that you’re developing a microblogging platform which only allows certain amount of characters in every post, and you’re not judged for being too lazy to write something longer than a sentence. In this hypothetical microblogging platform, you can mention other users in a post with “@” prefix and mention other posts with “#” prefix followed by the post’s identifier. You can even retrieve a post by typing its identifier in the search box. If you type in a username with “@” prefix in the search box, that user’s profile is shown.

Figure 1. Unvalidated data sources and places where you need to validate data repetitively
public class PostId
{
public int Value { get; private set; } #A
public PostId(int id) { #B
if (id <= 0) {
throw new ArgumentOutOfRangeException(nameof(id));
}
Value = id;
}
}

The style of code examples

Placement of curly braces is the second most debated topic in programming that hasn’t been settled in a consensus yet right after tabs vs spaces. I prefer Allman style for most C-like languages, like C# and Swift. Allman style is where every curly brace character resides on its own line. Swift officially recommends using 1TBS (One True Brace Style), aka improved K&R style, where an opening brace is on the same line with the declaration. People still feel the need to leave extra blank lines after every block declaration because 1TBS is too cramped. When you add blank lines, it effectively becomes Allman style, but people can’t bring themselves to admit it.

  • If you plan on comparing values yourself using equality operators (“==” and “!=”) you have to implement their operator overloads in the class.
  • If you plan to use it in a Dictionary<K,V> as a key, you need to override GetHashCode method.
  • String formatting functions such as String.Format uses ToString method to get a string representation of the class suitable for printing.

Don’t use operator overloading

Operator overloading is a way to change how operators like “==”, “!=”, “+”, and “-“ in a programming language behave. Developers who learn about operator overloading might go overboard and tend to create their own language with weird behavior for irrelevant classes like overloading “+=” operator to insert a record to a table with a syntax such as db += record. It’s almost impossible to understand the intent of such code. Don’t be that person. Even you’ll forget what it does and you’ll beat yourself up over this. Use operator overloading only to provide alternatives to equality and typecasting operators, and only when needed. Don’t waste time implementing them if they won’t be needed.

public class PostId
{
public int Value { get; private set; }
public PostId(int id) {
if (id <= 0) {
throw new ArgumentOutOfRangeException(nameof(id));
}
Value = id;
}
public override string ToString() => Value.ToString(); #A
public override int GetHashCode() => Value; #A
public override bool Equals(object obj) { #A
return obj is PostId other && other.Value == Value;
}
public static bool operator ==(PostId a, PostId b) { #B
return a.Equals(b);
}
public static bool operator !=(PostId a, PostId b) { #B
return !a.Equals(b);
}
}
public int Sum(int a, int b) {
return a + b;
}
public int Sum(int a, int b) => a + b;
  1. If you plan on comparing values using less than or greater than operators, you need to implement related operator overloads (“<”, “>”, “<=”, “>=”) for them too.
public class PostId: DbId {    #A
public PostId(int id): base(id) { }
}
public class TopicId: DbId { #A
public TopicId(int id) : base(id) { }
}
public class UserId: DbId { #A
public UserId(int id): base(id) { }
}

Don’t framework hard, framework smart

.NET, like many other frameworks, comes with a set of useful abstractions for certain data types which are usually unknown or ignored. Custom text-based values like URLs, IP addresses, file names, or even dates are stored as strings. We’ll look at some of those ready-made types and how we can use them

public string GetShortCode(string url)
{
const string urlValidationPattern =
@"^https?://([\w-]+.)+[\w-]+(/[\w- ./?%&=])?$"; #A
if (!Regex.IsMatch(url, urlValidationPattern)) {
return null; // not a valid URL
}
// take the part after the last slash
string[] parts = url.Split('/');
string lastPart = parts[^1]; #B
return lastPart;
}
public string GetShortCode(Uri url)    #A
{
string path = url.AbsolutePath; #B
if (path.Contains('/')) {
return null; #C
}
return path;
}
var testAddress = IPAddress.Loopback;
const int cacheExpiration = 5; // minutes
public const int cacheExpirationMinutes = 5;
cache.Add(key, value, cacheExpirationMinutes * 60);
public static readonly TimeSpan cacheExpiration = TimeSpan.FromMinutes(5);
cache.Add(key, value, cacheExpiration.TotalMinutes);
var now = DateTimeOffset.Now;
var birthDate =
new DateTimeOffset(1976, 12, 21, 02, 00, 00,
TimeSpan.FromHours(2));
TimeSpan timePassed = now - birthDate;
Console.WriteLine($"It’s been {timePassed.TotalSeconds} seconds since I was born!");

Types over typos

Writing code comments can be a chore. Even without the code comments, your code doesn’t have to lack descriptiveness. Types can help you to explain your code.

public int Move(int from, int to) {
// ... quite a code here
return 0;
}
public int MoveContents(int fromTopicId, int toTopicId) {
// ... quite a code here
return 0;
}
public MoveResult MoveContents(int fromTopicId, int toTopicId) {
// ... still quite a code here
return MoveResult.Success;
}
public enum MoveResult
{
Success,
Unauthorized,
AlreadyMoved
}
public MoveResult MoveContents(TopicId from, TopicId to) {
// ... still quite a code here
return MoveResult.Success;
}

Follow Manning Publications on Medium for free content and exclusive discounts.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store