Thursday, June 4, 2009

A Brief Aside on Type Conversion Errors

There are four similar errors in C# that are related to type conversions. Three of them are compiler errors, and one of them is a runtime error. I think it's high time to clear up the differences between them, and lay them side by side so they make sense to some of you who may be confused by the differences between them.

1. CS0030 (compiler error) "Cannot convert type "Foo" to "Bar" - This error occurs when trying to explicitly cast one type to a completely unrelated type. This can be reproduced with this code:


class Foo { }
class Bar { }

class Program
{
static void Main(string[] args)
{
Foo foo = new Foo();
Bar bar = (Bar)foo;
}
}


The part that the compiler is complaining about here is specifically the "(Bar)foo" code. There is no conversion, so it complains there.

2. CS0029 (compiler error) "Cannot implicitly convert type 'Foo' to 'Bar'" - This error occurs when trying to implicitly cast one type to a completely unrelated type. This is reproduced with the following code:


class Foo { }
class Bar { }

class Program
{
static void Main(string[] args)
{
Foo foo = new Foo();
Bar bar = foo;
}
}


This one is similar to the next, however, it doesn't let you know that an explicit conversion exists, and for good reason: there's not one. The compiler here is complaining about the entirety of the second line here, mainly the conflict between the declared type of Bar and the declared type of Foo, as the two have no conversion between them.

3. CS0266 (compiler error) "Cannot implicitly convert type 'double' to 'int' An explicit conversion exists (are you missing a cast?)" - This error will only occur in C# 2.0 and following. It was added in C# 2.0 to add a hint as to whether or not an explicit conversion exists. Think of this one as CS0029 with a tip. It occurs when a casting operation exists for a particular type, but the casting operation must be an explicit cast. This error can be remedied by simply adding an explicit cast. This can be reproduced with the following couple of examples:


class Program
{
static void Main(string[] args)
{
double d = 0;
int i = foo;
}
}


And the other:


class Foo { }
class Bar : Foo { }

class Program
{
static void Main(string[] args)
{
Bar foo = new Foo();
}
}


In both of these situations, a conversion does exist, but you're simply not using it. This is used in situations where an implicit conversion doesn't exist for the type, but an explicit one does. This is a tricky error, though, because in some situations, the cast might cause the compiler to shuttup, only to make the application crash at runtime with the next exception I'll explain.

4. InvalidCastException (runtime error) "Unable to cast object of type 'Foo' to type 'Bar'" - This error occurs when a variable of a parent type is cast to a variable an inherited type, but the constructed type is actually that of the parent, or of a type inherited from the parent that is not within the inheritance tree of the type to which the cast is attempted. This can be reproduced with the following code:


class Foo { }
class Bar : Foo { }

class Program
{
static void Main(string[] args)
{
Foo foo = new Foo();
Bar bar = (Bar)foo;
}
}


The complaint happens not because of the declared type of foo, but because of it's the instantiated type. The instantiated type of Foo is Foo in this example. If I change that first line to the following the error will disappear:


Foo foo = new Bar();


The error here goes away, because I've changed the instantiated type of "foo" to Bar, so the conversion is valid, and the runtime marches onward.

For a full reference of all the compiler errors, check out this link.