14

In C#, we can overload the implicit conversion operator like this (example from MSDN):

struct Digit
{
    /* ... */
    public static implicit operator byte(Digit d)  // implicit digit to byte conversion operator
    {
        /* ... */
    }
}

Thus, we can have a type, a custom value type, magically converting itself to another (unrelated) type, leaving the audience in bewilderment (until they peer into the backstage and see the implicit conversion operator, that is).

I don't like leaving anyone who reads my code in bewilderment. I don't think many people do.

The question is, what are the use-cases of the implicit type conversion operator which won't render my code much more difficult to understand?

3
  • 1
    Wow. I actually didn't know this existed. Not that it's necessarily a good thing to use; I know people got really annoyed at this kind of functionality-hiding in C++.
    – Katana314
    May 19, 2015 at 21:23
  • @Katana314: That wasn't what people got annoyed about, but about someone adding an overload (be it operator, conversion-function, constructor, free function or member-function) with surprising behavior, preferably subtly surprising. May 19, 2015 at 22:00
  • I recommend you read up on "operator overloading" in C++, specifically the "casting" operators. I suspect many of the same arguments for/against are the same, except the debate has been going on three times as long as C# has existed with a lot more to read.
    – user22815
    May 19, 2015 at 23:37

2 Answers 2

19

I would only recommend implicit conversions between types that roughly represent the same values in different ways. For instance:

  • Different color types like RGB, HSL, HSV and CMYK.
  • Different units for the same physical quantity (Meter vs Inch).
  • Different coordinate systems (polar vs Cartesian).

However, there are some strong guidelines that indicate when it is not appropriate to define an implicit conversion:

  • If the conversion causes a significant loss of precision or range, then it shouldn't be implicit (e.g.: from float64 to float32 or from long to int).
  • If the conversion can throw an (InvalidCast) exception, then it shouldn't be implicit.
  • If the conversion causes a heap allocation each time it is performed, then it shouldn't be implicit.
  • If the conversion is not an O(1) operation, then it shouldn't be implicit.
  • If the source type or the target type is mutable, then the conversion shouldn't be implicit.
  • If the conversion is dependent on some sort of context (database, culture settings, configuration, file system, etc.) then it shouldn't be implicit (I would also discourage an explicit conversion operator in this case).

Now say your conversion operator f: T1 -> T2 doesn't violate any of the rules above, then the following behavior strongly indicates that the conversion can be implicit:

  • If a == b then f(a) == f(b).
  • If a != b then f(a) != f(b).
  • If a.ToString() == b.ToString() then f(a).ToString() == f(b).ToString().
  • Etc. for other operations that are defined on both T1 and T2.
9
  • All your examples are probably lossy. Whether they are exact enough anyway, ... May 20, 2015 at 0:18
  • Yeah I realized that :-). I couldn't think of a better term for "lossy". What I meant by "lossy" are conversions where the range or precision is substantially reduced. E.g. from float64 to float32 or from long to int. May 20, 2015 at 1:10
  • I think a !=b => f(a) != f(b), probably shouldn't apply. There are plenty of functions that might return the same value for different inputs, floor() and ceil() for example on the math side
    – cdkMoose
    May 20, 2015 at 20:57
  • @cdkMoose You are right of course, and that's why I see these properties more as "bonus points", not rules. The second property simply means that the conversion function is injective. This is often the case when you convert to a type that has a strictly larger range, e.g. from int32 to int64. May 20, 2015 at 22:24
  • @cdkMoose On the other hand, the first property just states that two values within the same equivalence class of T1 (implied by the == relation on T1) always map to two values within the same equivalence class of T2. Now that I think about it, I guess the first property actually should be required for an implicit conversion. May 20, 2015 at 22:38
6

The question is, what are the use-cases of the implicit type conversion operator which won't render my code much more difficult to understand?

When the types aren't unrelated (to programmers). There are (rare) scenarios where you have two unrelated types (as far as the code is concerned), that are actually related (as far as the domain or reasonable programmers) are concerned.

For example, some code to do string matching. A common scenario is to match a string literal. Rather than calling IsMatch(input, new Literal("some string")), an implicit conversion lets you get rid of that ceremony - the noise in the code - and focus on the string literal.

Most any programmer will see IsMatch(input, "some string") and quickly intuit what is going on. It makes your code clearer at the call site. In short, it makes it a bit easier to understand what is going on, at a slight expense of how that is going on.

Now, you might argue that a simple function overload to do the same thing would be better. And it is. But if this sort of thing is ubiquitous, then having one conversion is cleaner (less code, increased consistency) than doing a pile of function overloads.

And you might argue that it is better to require programmers to explicitly create the intermediate type so they see "what is really going on". That is less straightforward. Personally, I think that the literal string match example is very clear about "what is really going on" - the programmer needs not know the mechanics of how everything happens. Do you know how all your code is executed by the various processors your code runs on? There is always a line of abstraction where programmers stop caring about how something works. If you think that the implicit conversion steps are important, then don't use the implicit conversion. If you think they're just ceremony to keep the computer happy, and the programmer would be better off not seeing that noise everywhere, then consider it.

1
  • Your last point can and should be taken even further: There's also a line beyond which a programmer had damn well better not care how something is done, because that's not contractual. May 19, 2015 at 22:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.