Implicit conversion

S

Steve Gough

Could anyone please help me to understand what is happening here? The
commented line produces an error, which is what I expected given that
there is no conversion defined from type double to type Test. I
expected the same error from the following line, but it compiles fine.
The double is silently truncated to an int and then fed in to the
implicit conversion operator. Why does this happen? Is there any way
that I can keep the implicit conversion from int but guarantee a
compiler error if an attempt is made to cast from double?

class Test
{
public static implicit operator Test(int x)
{
return new Test();
}
}

class Driver
{
public static void Main()
{
//Test y = 4.5; // error
Test x = (Test)4.5; // this compiles
}
}

Many thanks

Steve Gough
 
J

Joe Mayo

Steve Gough said:
Could anyone please help me to understand what is happening here? The
commented line produces an error, which is what I expected given that
there is no conversion defined from type double to type Test. I
expected the same error from the following line, but it compiles fine.
The double is silently truncated to an int and then fed in to the
implicit conversion operator. Why does this happen? Is there any way
that I can keep the implicit conversion from int but guarantee a
compiler error if an attempt is made to cast from double?

class Test
{
public static implicit operator Test(int x)
{
return new Test();
}
}

class Driver
{
public static void Main()
{
//Test y = 4.5; // error
Test x = (Test)4.5; // this compiles
}
}


Hi Steve,

First, consider that there are implicit conversions and explicit
conversions. Implicit happens when there is no loss of information or an
exception can't be thrown. For example, and implicit conversion exists from
int to double because the value copied is preserved, but there is no
implicit conversion from double to int because you would loose precision.
This is why explicit conversions exist. They force you to say that if I
cast this double (4.5) to an int, I know that I am going to loose precision
(4).

When doing a conversion, C# looks for an operator that will allow it to
convert from the source type to a target type. It will first look to see if
there is an exact match of the source and target types. In your case, there
is not an exact match because 4.5 is a double and your conversion operator
expects an int. So, the next thing C# will do is see if there is a
conversion from the source type (double) to a type in any of the available
conversion operators (int).

In your first example (the one with the error), you are performing an
implicit conversion (no cast). So, C# is looking for an available
conversion operator in Test that will allow you to perform the implicit
conversion that you asked for. The only available conversion is for an int.
Since there is no implicit conversion from double to int, you get a compile
time error. This is as it should be.

Your second example succeeds because you specified (with the cast) that you
want to perform an explicit conversion. Since there is an explicit
conversion from double to int, C# will match your conversion operator in
Test. The conversion from double to int will occur, which truncates your
precision. Then the conversion operator on Test will execute. Since you
specified an explicit conversion, C# interpreted it as something you wanted
to do, regardless of the result.

Joe
 
G

GMorris

I'm sorry, but this just makes me absolutely NUTS!
implicit conversion from double to int because you would <loose> precision.
cast this double (4.5) to an int, I know that I am going to <loose>
precision

Please, everyone, look at this:

lose: (looz), to be unsuccessful in retaining possession of, mislay

loose: (loos), not fastened, detached, etc.

As in: Don't lose your virginity to a loose woman.

That really drives me crazy above all other grammatical errors!
 
J

Joe Mayo

Thank you for that correction. I really must refrain from being loose with
my grammar. Otherwise it would cause some people to get uptight. ;)

Joe
 
G

GMorris

It's not just you, I see this constantly and it throws me for a loop
every time. I'm starting to get used to it, but I still have to stop and
re-read sentences that use loose for lose. Sometimes I just can't
stand it anymore and say something, hoping that others who make
the same error will see it and think about it. Maybe...

Sorry.
 
P

Peter Bromberg

Definitely one of my pet peeves too, and also an indication of option
explicit and option strict never having been enforced by one's
grammatical upbringing! <grin/> Bottom line is, if you are a
professional software developer and possess an above - room -
temperature IQ, there's no excuse for not learning the syntax of your
own native language, English. . .

Same with "Like" and "as"....
 
J

Jeff Louie

Peter... I have mixed feelings about the grammar police. An occassional
correction is OK, but this is a C# newsgroup, not an English newsgroup.

Regards,
Jeff
Definitely one of my pet peeves too, and also an indication of option
explicit and option strict never having been enforced by one's
grammatical upbringing! <grin/><
 
S

Steve Gough

<helpful explanation>

Many thanks for your explanation, Joe - it helps a lot. I must admit,
though, that I am slightly surprised by this behaviour. I know that I
am explicitly asking for a conversion, but the conversion that I'm
explicitly asking for is from double to Test. What I actually get are
two conversions that I didn't ask for at all - one from double to int,
and then another from int to Test. What puzzles me is how the
potentially dangerous conversion from double to int gets silently
slipped into the equation.

If you folks will indulge my ignorance for a little longer, here's a
further example that muddies the waters for me. What follows is
another version of the original example, but this time using
user-defined types (MyInt, MyDouble) in place of built-in types. As
per the built-in types I've created an implicit conversion from MyInt
to MyDouble and an explicit conversion from MyDouble to MyInt. Based
on the earlier example and on Joe's explanation I had expected to see
a silent conversion from MyDouble to MyInt, and then from MyInt to
Test. What I actually get is the error that I originally expected to
get in the first example; namely, the explicit conversion from
MyDouble to Test is not possible.


class Test
{
public static implicit operator Test(MyInt i)
{
return new Test();
}
}

struct MyInt
{
public static implicit operator MyDouble(MyInt i)
{
return new MyDouble();
}
}

struct MyDouble
{
public static explicit operator MyInt(MyDouble d)
{
return new MyInt();
}
}

class Driver
{
public static void Main()
{
MyDouble d = new MyDouble();
//Test t = d; // error, as expected.
//Test t = (Test)d; // error for MyDouble/MyInt; why not for
double/int?
}
}

Thanks again

Steve Gough
 
P

Picho

Steve,

First of all, please don't correct my grammer, since English is not my
native language... sorry.

I understand your requierment regarding the compiler error. I have puzzled
myself with your problem again and again but found no possible reason for
this problem.
On the other hand, and even though I am aware that cast operators should
generaly speaking never throw exceptions, if you do provide an
implicit/explicit casting from double and in it throw an exception, you are
still guarentied that the cast will never happen (at run-time :(...):

class Class1

{

[STAThread]

static void Main(string[] args)

{

//Test y = 4.5; // error

Test Y = 5;

Test x = 4.5; // this throws an exception

}

}

class Test

{

public static implicit operator Test(int x)

{

return new Test();

}

public static implicit operator Test(double x)

{

throw new InvalidCastException("Cannot cast from double to Test");

}

}
 
J

Joe Mayo

The reason the explicit conversion succeeded in the original case was
because there was a standard conversion from double to int. There are two
categories of conversions, standard and user-defined. Standard conversions
are defined by C# and user-defined conversions are the conversion operators
that you add to your types. In my explanation, I introduce the notion of a
'single conversion operation', for which there is not an official
definition. However, I use the term for the purpose of explanation because
I feel it is central to identifying the problem and the work around. Here's
a short overview of how the conversion process works (a single conversion
operation):

1. Perform a standard conversion, if necessary, to the parameter type of an
available user-defined conversion.
2. Perform the user-defined conversion that matches a type after a standard
conversion occurs or with whatever type matched without a standard
conversion.
3. Perform a standard conversion from the results of the user-defined
conversion to the target type of the conversion.

Notice #2 where there is exactly one "user-defined" conversion. The
original case succeeded because there was 1) a standard explicit conversion
from double to int, 2) a user-defined conversion from int to Test, and 3) a
standard conversion was not required because the conversion operator used
already returned the target type specified.

Now, notice how the case with your user-defined types and user-defined
conversions is structured. Here you are trying to perform 1) a user-defined
conversion from MyDouble to MyInt, 2) a user-defined conversion from MyInt
to Test, and <kaboom> you won't get to 3 because C# will detect that what
you are doing is illegal and generate a compiler error. You are not allowed
to perform two or more user-defined conversions in a single conversion
operation. In other words, you can only have one user-defined conversion in
any single conversion operation.

Here's how to fix it:

Test t = (Test)(MyInt)d;

This performs a single user-defined conversion from MyDouble to MyInt, which
is a single conversion operation. Then it performs a user-defined
conversion from MyInt to Test, which is another single conversion operation.

On another note, for those of you who are enjoying my grammar mistakes in
this thread, please forgive any further transgressions. In particular, I
have been unable to identify the correct spelling of <kaboom>. <grin>

Joe
 
J

Joe Mayo

Hi Picho,

I think you have it the other way around. Implicit conversions, which don't
require a cast, should never throw an exception and should never lose
information. On the other hand, the purpose of explict conversions is to
force the developer to make a concious decision when casting one type to
another when the results may result in data loss. Explicit conversions can
throw exceptions.

Also, please see my response to Steve for an explanation of why he is seeing
the behavior that appears in his sample code.

As far as grammar goes, I've been around the world and butchered many
languages in my path. I've always appreciated the grace and patience that
people in other countries extend to me when communicating. For me, there is
no excuse for making mistakes in my native language. (<joking>For the
grammar police: Go ahead and frame that and put it on the wall if it makes
you happy. </joking>) However, these groups bring the whole world together
in a community of people interested and passionate about their profession.
I would only hope that others practice tolerance and continue to embrace and
welcome everyone regardless of where in this small world they come from
and/or how well they communicate. :)

Joe
 
S

Steve Gough

Joe Mayo said:
The reason the explicit conversion succeeded in the original case was
because there was a standard conversion from double to int.

Ahhh - I think I finally understand what is happening. Many thanks,
Joe, for your very clear explanations. I was missing the role that
these standard coversions play. Thanks too to Picho for your
suggestion - the addition of an explicit conversion from double which
throws an exception does indeed trap the unwanted conversion, albeit
at runtime rather than compile time.
Thanks all,
Steve
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top