Implicit Operators – Clean Code Secrets Or Buggy Nightmare?

Welcome to the world of implicit operators in C#! An implicit operator is a powerful feature of the C# language that has the ability to make our code more readable and expressive. In the wrong hands, implicit operators can allow for some really tricky behavior that’s hard to notice unless you go investigating. Implicit operators can allow us to define custom conversions that occur without explicit casting, and thus eliminate some clunky syntax when we want to convert between types. This might seem like a small thing, but it can have a big impact on the readability and maintainability of our code.

Implicit operators are defined using the implicit keyword and can be used to create a method that converts one type to another. Two key rules of implicit operators are that they should not throw exceptions and should not lose information, as the conversion is done automatically by the compiler. This is because the compiler needs to be able to guarantee that the conversion can always succeed without any potential for data loss or exceptions.


What’s In This Article: Implicit Operators in C#


Understanding Implicit Operators in C#

Implicit operators are special methods we can define in our classes to allow for automatic conversion to another type. They are defined using the implicit keyword. This keyword is used to modify the operator keyword in the method signature, indicating that the operator will be applied implicitly.

Here’s a simple example:

public readonly record struct Meter
{
    public double Length { get; init; }

    public static implicit operator Meter(double length)
    {
        return new Meter { Length = length };
    }
}

// Usage
Meter meter = 10.0;
Console.WriteLine(meter); // Meter { Length = 10 }

In this example, we’ve defined an implicit operator that allows us to convert a double to a Meter object. This is done without any explicit casting or conversion in the code, hence the term “implicit operator”. The compiler automatically applies the operator when it’s needed, and we don’t need to explicitly cast with (double) or (meter) in front of our variables.


The Power of Implicit Operators

Implicit operators can be incredibly powerful. They allow us to create code that is more expressive and easier to read. By defining implicit operators, we can create types that feel like a natural part of the language, rather than something that’s been bolted on. This can be a popular solution for looking into strongly typed identifiers as well when you want to create types that naturally fit into the domain you’re working in.

For example, consider a Money type. Without implicit operators, working with a Money type might look something like this:

Money m = new Money(10.0m);
decimal amount = m.Amount;

But with implicit operators, we can make this code much more natural and intuitive:

Money m = 10.0m;
decimal amount = m;

This might seem like a small change, but it can make a big difference in the readability of our code. And given that we, as software developers, spend much of our time reading code compared to writing it… Readability should be optimized for!


Companion Video for Implicit Operators

Check out this video on implicit operators in C# where I walk through code examples!

YouTube player

Practical Applications of Implicit Operators

Implicit operators can be used to improve the readability of your code and to create more intuitive APIs. They are particularly useful when working with domain-specifics or when you want to provide a simple interface for working with complex types. I even used them for creating custom return types that make my exception handling much easier!

For example, you could define implicit operators for a Money type that allow you to work with money values as if they were numbers:

public readonly record struct Money
{
    public decimal Amount { get; init; }

    public static implicit operator Money(decimal amount)
    {
        return new Money { Amount = amount };
    }

    public static implicit operator decimal(Money money)
    {
        return money.Amount;
    }
}

// Usage
Money money = 10.0m;
decimal amount = money;

In this example, we can assign a decimal to a Money object and vice versa, making the Money type feel more like a natural part of the domain you’re working in.


Advanced Usage of Implicit Operators

While implicit operators are powerful, they should be used judiciously. Overuse of implicit operators can lead to code that is difficult to understand and maintain. That sounds counter to what we said earlier, right? Well, the code may be easy to read and this allows us to make assumptions about what is happening. However, if the actual behavior doesn’t line up with our expectations about what we’re reading, suddenly we have a much worse problem than hard-to-read code! It’s also important to remember that implicit operators should not throw exceptions or lose information, which ties into this point exactly.

Here’s an example of a more advanced usage of implicit operators, where we define an implicit operator for a Complex number type:

public readonly record struct Complex
{
    public double Real { get; init; }
    public double Imaginary { get; init; }

    public static implicit operator Complex(double real)
    {
        return new Complex { Real = real };
    }

    public static implicit operator Complex((double real, double imaginary) tuple)
    {
        return new Complex { Real = tuple.real, Imaginary = tuple.imaginary };
    }
}

// Usage
Complex complex1 = 10.0;
Complex complex2 = (10.0, 20.0);

In this example, we can assign a double or a tuple to a Complex object, providing a flexible interface for creating complex numbers. The options for this become seemingly endless when you think about all of the different domains that exist. What about something for working with quantities and doing unit conversions?!


Pitfalls and Things to Avoid

While implicit operators can be incredibly useful, they can also be a source of confusion if not used carefully. One of the main things to avoid is creating implicit operators that can result in data loss or that can throw exceptions. Because implicit operators are applied automatically by the compiler, any exceptions they throw can be difficult to trace.

For example, if we were to have an invalid cast exception thrown because of an improper type conversion operation, we’d be used to seeing a line of code with an explicit cast operation. We’d see in parentheses the type we are trying to cast to being applied to a variable or value, and know right away there was some type of incompatibility as suggested by the exception pointing to that line number. With implicit operators, the conversion happens “magically” behind the scenes for us so if there’s any sort of error as a result of the operator executing:

  1. We not only have to pause to recognize there’s some type of implicit operator being applied (i.e. “Wait a second, how is a double value being assigned to this type that is not a double?!”)
  2. We then have to go into that type’s code (let’s hope we have the source for it!) and then go diagnose what’s wrong with the logic.

Another thing to avoid is creating implicit operators that can result in unexpected behavior. For example, if you create an implicit operator that converts a string to an int, it might be unclear whether the conversion is parsing the string as a number or getting the length of the string. Remember from earlier we touched on readability being paramount? Well, this remains true. But if the behavior does not align with what the reader is understanding from the code in front of them, then this will create a lot of confusion.


Wrapping Up Implicit Operators in C#

Congratulations! You’ve just learned about implicit operators in C#, a powerful feature that can make your code more readable and expressive. Remember, while implicit operators are powerful, they should be used with care. Always ensure that your implicit operators do not throw exceptions or lose information. And most importantly, make sure the behavior of your operators is aligned with how the reader of the code would expect things to happen.

Thanks for reading, and consider checking out this article if you want to see how I made a multi-type object for dealing with exceptions using implicit operators! You can also head over to this video here for a tutorial on the topic that might help solidify some of the concepts. And finally, if you enjoy topics like this and would like a quick weekly summary on software engineering and other C# topics, you should totally check out Dev Leader Weekly! My newsletter is a quick 5-minute read every weekend for you to catch up on some software engineering and C# topics.

Affiliations:

These are products & services that I trust, use, and love. I get a kickback if you decide to use my links. There’s no pressure, but I only promote things that I like to use!

      • RackNerd: Cheap VPS hosting options that I love for low-resource usage!
      • Contabo: Alternative VPS hosting options with very affordable prices!
      • ConvertKit: This is the platform that I use for my newsletter!
      • SparkLoop: This service helps me add different value to my newsletter!
      • Opus Clip: This is what I use for help creating my short-form videos!
      • Newegg: For all sorts of computer components!
      • Bulk Supplements: For an enormous selection of health supplements!
      • Quora: I try to answer questions on Quora when folks request them of me!


    Frequently Asked Questions: Implicit Operators in C#

    What are implicit operators in C#?

    Implicit operators in C# are a feature that allows for automatic conversion between different data types, making code more readable and expressive.

    Why should implicit operators be used carefully in C#?

    Implicit operators should be used carefully to avoid issues like data loss or exceptions. Since they are automatically applied by the compiler, any issues can be hard to trace and diagnose.

    Can implicit operators in C# lead to unexpected behavior?

    Yes, implicit operators can lead to unexpected behavior. For example, a conversion from ‘string’ to ‘int’ might be unclear in its operation, leading to confusion for the code reader.

    What are the key considerations when creating implicit operators in C#?

    When creating implicit operators, ensure they do not throw exceptions or lose information. The behavior should align with reader expectations to maintain code readability.

    Where can I find more resources on implicit operators in C#?

    You can find more resources on implicit operators in C#, including example code and tutorials, on DevLeader.ca and the Dev Leader YouTube channel. MSDN is also a great spot to research more.

    author avatar
    Nick Cosentino Principal Software Engineering Manager
    Principal Software Engineering Manager at Microsoft. Views are my own.

    This Post Has 2 Comments

    1. Keith Thompson

      I suggest that referring to “implicit conversions” rather than “implicit operators” would have been much clearer.

      When I read the title via a link on Hacker News, I was expecting something like the mathematical notation of using “xy” to mean “x * y”.

      1. Nick Cosentino

        I appreciate the feedback 🙂 The syntax in the language itself is “static implicit operator”, so I opted to refer to these exactly as they are called in the language. I felt like if anyone was searching for information on these (which is how I need to organically draw traffic to my blog), then sticking to the terminology as it’s defined in the language was the best option.

    Leave a Reply