8

I have got a struct:

public struct Decibel
{
    public readonly double Value;

    public Decibel (double value)
    {
        Value = value;
    }

    public static implicit operator Decibel (double decibels)
    {
        return new Decibel (decibels);
    }

    public static implicit operator double (Decibel decibels)
    {
        return decibels.Value;
    }
}

And now I can do:

bool x = new Decibel (0) < new Decibel (1);

It seems the compiler is smart enough to convert Decibel to double, and then use the < operator for doubles?

I had different struct named Duration, which was wrapping TimeSpan. It had implicit conversions for TimeSpan to/from Duration, but the < and > operators are not working for it.

Does c# only recognize conversion between primitive types?

James Thorpe
  • 31,411
  • 5
  • 72
  • 93
apocalypse
  • 5,764
  • 9
  • 47
  • 95
  • There is related information from Eric Lippert here: http://stackoverflow.com/a/8529811/106159 – Matthew Watson Apr 29 '15 at 09:01
  • `implicit` conversions make it very easy to shoot yourself in the foot. They should be used rarely and carefully. And an implicit conversion to a primitive type is just a bad idea. – H H Apr 29 '15 at 09:41
  • @HenkHolterman: i know, im trying it use carefuly :) – apocalypse Apr 29 '15 at 11:23

1 Answers1

3

Firstly note that C# will allow a maximum of ONE implicit user-defined conversion between types.

So when you compare two instances of Decibel, the compiler sees that it can use a user-defined implicit conversion to convert a Decibel to a double with which to compare.

However when you compare two instances of Duration, the compiler cannot find any single implicit conversion which it can use to allow the comparison. The compiler will not consider any user-defined comparison operators for any of the types to which the type can be implicitly converted. It will only look for built-in comparison operators for any of the types to which the type can be implicitly converted.

Therefore the compiler will not use the implicit conversion to TimeSpan, even though TimeSpan provides a user-defined comparison operator that could theoretically be used.

Also note that even if the TimeSpan class provided an implicit conversion to double, the compiler would still not use it, since it will only consider at most one implicit user-defined conversion in the chain of implicit conversions.

In other words, given these structs:

public struct Number
{
    public readonly double Value;

    public Number(double value)
    {
        Value = value;
    }

    public static implicit operator Number(double duration)
    {
        return new Number(duration);
    }

    public static implicit operator double(Number number)
    {
        return number.Value;
    }
}

public struct NumberWrapper
{
    public readonly Number Value;

    public NumberWrapper(Number value)
    {
        Value = value;
    }

    public static implicit operator NumberWrapper(Number duration)
    {
        return new NumberWrapper(duration);
    }

    public static implicit operator Number(NumberWrapper number)
    {
        return number.Value;
    }
}

This code will compile:

bool x = new Number(1) < new Number(2);

And so of course will this:

Number n1 = new NumberWrapper(1);
Number n2 = new NumberWrapper(2);
bool z = n1 < n2;

But this won't:

bool y = new NumberWrapper(1) < new NumberWrapper(2);

because NumberWrapper doesn't have an implicit conversion to any type that supports < without any further implicit conversions.

Note that all primitive numeric and enumeration types (e.g. char, short, int, long, float, double, decimal, enum) provide built-in comparison operators. All other types can only provide user-defined comparison operators.

User-defined comparison operators look like this:

public static bool operator < (MyType lhs, MyType rhs) ...
Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
  • Maybe you should explain the difference between "user-defined comparison operators" and "built-in comparison operators" just to make it clearer. – Magnus Apr 29 '15 at 09:54
  • @Magnus: yeah, are operator for TimeSpan are build int? Or built in are operators for primitive types? – apocalypse Apr 29 '15 at 11:24
  • @zgnilec The operators are only built-in for primitive types (as I put at the end of my answer) – Matthew Watson Apr 29 '15 at 11:51