Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do I need an intermediate conversion to go from struct to decimal, but not struct to int?

Tags:

operators

c#

I have a struct like this, with an explicit conversion to float:

struct TwFix32
{
    public static explicit operator float(TwFix32 x) { ... }
}

I can convert a TwFix32 to int with a single explicit cast: (int)fix32

But to convert it to decimal, I have to use two casts: (decimal)(float)fix32

There is no implicit conversion from float to either int or decimal. Why does the compiler let me omit the intermediate cast to float when I'm going to int, but not when I'm going to decimal?

like image 532
Jesse McGrew Avatar asked May 10 '10 22:05

Jesse McGrew


1 Answers

I am often at a loss to give a satisfactory answer to "why" questions.

The reason that the C# compiler exhibits that behaviour is because it is (in this case at least (*)) a correct implementation of the C# specification.

Section 6.4.5 of the specification describes how user-defined conversions are analyzed. A careful reading of that section will explain why the explicit conversion to int is legal but to decimal is not.

Specifically, the relevant paragraph is:

Find the set of applicable user-defined and lifted conversion operators, U. This set consists of the user-defined and lifted implicit or explicit conversion operators declared by the classes or structs in D that convert from a type encompassing or encompassed by S to a type encompassing or encompassed by T. If U is empty, the conversion is undefined and a compile-time error occurs.

In your case, S is TwFix and T is either int or decimal. The only user-defined explicit conversion on TwFix returns a float. int is encompassed by float, but decimal is neither encompassed by nor encompasses float. Therefore the set U has a member in one case and is empty in the other. Therefore one case produces an error, as the specification says to do, and the other does not.

I have the feeling that this answer is not satisfactory. If not, can you rephrase the question so that it doesn't have the word "why" in it? I'm a lot better at answering "what" or "how" questions than "why" questions.

(*) The compiler has known bugs in the code which computes whether one type encompasses another for the purpose of determining what built-in conversions are relevant when analyzing the semantics of a particular user-defined conversion. In many cases we're deliberately not fixing these bugs because doing so would introduce a breaking change in real-world code for no great benefit. I would like very much to revisit this section of the specification and rewrite it so as to remove the concept of "encompassing type"; it's a bit of an oddity in the spec. And, as you've discovered, it produces this oddity, where float is explicitly convertible to decimal and decimal is explicitly convertible to float, but since neither encompasses the other, the user-defined explicit conversion code doesn't like it. However this is very low priority.

like image 117
Eric Lippert Avatar answered Nov 07 '22 17:11

Eric Lippert