Question

What are general guidelines on when user-defined implicit conversion could, should, or should not be defined?

I mean things like, for example, "an implicit conversion should never lose information", "an implicit conversion should never throw exceptions", or "an implicit conversion should never instantiate new objects". I am pretty sure the first one is correct, the third one is not (or we could only ever have implicit conversion to structs), and I don't know about the second one.

Was it helpful?

Solution

The first isn't as simple as you might expect. Here's an example:

using System;

class Test
{
    static void Main()
    {
        long firstLong = long.MaxValue - 2;
        long secondLong = firstLong - 1;

        double firstDouble = firstLong;
        double secondDouble = secondLong;

        // Prints False as expected
        Console.WriteLine(firstLong == secondLong);

        // Prints True!
        Console.WriteLine(firstDouble == secondDouble);        
    }
}

Personally I very rarely create my own implicit conversions. I'm happy enough with the ones in the framework, but I rarely feel that adding my own would make life better. (The same is true of value types in general, btw.)

EDIT: Just to actually answer the question a bit, it's probably worth reading the conversion operators part of the Microsoft class library design guidelines.

OTHER TIPS

I'd agree with the first definitely - the second most of the time ("never say never"), but wouldn't get excited by the third; apart from anything else, it creates an unnecessary distinction between structs and classes. In some cases, having an implicit conversion can vastly simplify a calling convention.

For explicit conversions, all things are possible.

It isn't that often you need to write conversion operators, though. And in many cases, explicit operators are more useful, with the acceptance that they can break if conditions aren't right (for example, with Nullable<T>, if they don't have a value).

If you will only be supporting implicit conversions among objects of your own types, I would suggest that you should decide on what "axioms" should apply to such conversions (e.g. it may be helpful to decide that if a==b, b==c, and a==c are all legal and two of them are true, the third must also be true) and then identify a the most useful set of conversions which would make those axioms hold. I discuss four useful axioms on my blog at http://supercatnet.blogspot.com/2013/09/axioms-of-implicit-type-conversion.html (unfortunately, the .NET Framework includes conversions which violate all four, while disallows conversions which wouldn't have to).

An important thing to consider is that implicit conversions to imprecise types will in most contexts pose a smaller risk of astonishment than than conversions from imprecise types, but there's a notable exception: if something of a more-precise type is fed to a method or operator which has overloads that take both the more-precise type and a less-precise type to which it can be converted, some level of "astonishing" behavior will be almost inevitable unless one can make implicit conversion impossible [e.g. the behavior if programmer writes if (someLong == someFloat) may be astonishing, but not because the loss of precision on an implicit long to float conversion is astonishing, but rather because there are at least six different ways one might want to compare a long and a float(*), and any meaning the compiler might attach to a direct comparison would astonish those who expected something else. The only solution I know of to avoid such astonishment is to provide overloads to explicitly cover all ambiguous cases and mark them with an [Obsolete()] tag. Doing that is apt to be somewhat awkward, but would offer the substantial advantage of being able to satisfy all four axioms.

(*) A programmer might plausibly be intending to test whether the long was equal to the value of the float rounded to nearest, truncated toward zero, or floored toward negative infinity; alternatively, the programmer might be wanting to test whether the float was the one that represents the long, whether the float and long have the same nominal value, or whether the float and long would both convert to the same double.

The borderline case which caused me to ask this question is a violation of the second one. Namely, I have a Lazy<T> class (doesn't everyone?) and started pondering whether I shouldn't provide an implicit conversion to T. My instinct is to say yes, and the current version does, but I am not too sure.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top