C# also supports both implicit and explicit casts and gives you some different choices when you need to switch types. Plus you have a runtime that’s ready to throw an InvalidCastException. But even if you don’t hit an exception, there’re some things you should be aware of. This is still casting.
This is a short episode that builds on the previous episode and describes how you cast types in C#. You don’t have initializer lists or named casts like in C++. But you do have a runtime that will help make sure that your casts are well behaved. If not, the runtime will throw an InvalidCastException.
You have four options to perform casts in C#:
- You can use C-style casts.
- You can define an operator in your class that knows how to return the type you want to cast to.
- You can implement the IConvertible interface if you want your class to be able to be converted to base types such as int.
- You can implement a Parse or a TryParse method if you want to be able to convert to your class from a string. This of course requires that your class be able to generate a suitable string in the first place that the Parse method can use to reconstruct your class.
And you also have two keyword operators in C# to help you test and cast types. The “is” operator allows you to test if an instance really is another type. And the “as” operator allows you to perform a cast but if the cast fails, then instead of throwing an InvalidCastException, the “as” operator will return null.
Listen to the episode or you can also read the full transcript below.
The C# compiler is much better about only performing implicit casts from smaller values to larger integral types and from derived classes to base classes.
You don’t need to do anything special to let the compiler convert types implicitly. For other conversions, you have four options.
#1. Use a cast. The syntax follows the C style casts that you learned about in the previous episode. This is unfortunate because it does make these casts harder to find in your code. You can use this cast whenever you might lose information due to narrowing or if the cast might not succeed. If the cast doesn’t succeed, it’ll throw an InvalidCastException. We’ll talk about exceptions and what it means to throw and catch exceptions in a future episode. The casts in C# are less dangerous than in C++ and won’t let you convert or cast one type to a completely unrelated type. However, you can use the next option to get around this limitation.
#2. Define an operator in your class that knows how to return the type you want to cast to. Let’s say you have an adventure game with a dragon class and you want to allow instances of dragon to be converted to bool. The compiler would have no way of doing this on its own and just casting to bool directly is a really bad idea. So instead, you can declare an operator bool method in your dragon class that knows how to look at the health of the dragon. If the dragon is still alive, then it returns true. And if the dragon is dead, then it returns false. You could of course have a method called IsAlive instead that would be much more intuitive and leave out the suspicious conversion to bool. Use this technique only when the conversion makes sense.
#3. You could implement the IConvertible interface to allow the System.Convert class to convert your class to various base types. Your implementation will throw an InvalidCastException for any base types that you don’t support.
#4. You can implement various methods called Parse or TryParse that are commonly used to read and convert strings to a different type. You can use this to save all the important information about a dragon to a string and then be able to reconstruct that dragon just like before.
In addition to these four options, C# gives you two special keywords called “is” and “as” to help you.
The “is” keyword allows you to test if one type really is another type or not. It returns true if the type is the other type and false if not. Use this when you just want a quick test to see if something is what you think it might be. If you then want to actually cast the type to the other type, then you don’t need to use the “is” keyword. And in fact, I’ll actually urge you to not do a test for a type followed by a cast to that type because you could end up in a situation where the type of the object changes between the test and the cast. You code then thinks it tested the type and it’s now safe to perform the cast. Then when it gets to the cast, it throws an exception. You’ll be unlikely to hit this scenario unless you’re writing code with multiple threads. We’ll talk about multithreading in several future episodes. It’s a big topic. But for now, just know that multithreading allows your program to perform multiple actions all at the same time and sometimes these actions can interfere with each other if you’re not careful.
The “as” keyword allows you to cast a type to another type but instead of throwing an exception if the cast fails, your result will be null. If you get a result that’s not null, then the cast worked and you can use it. This is a safer approach than the previous method in case where you actually want to try using the converted type. Just assign the result of the “as” operation to a local variable of the new type and then test the variable for null before using it.
This works great for reference types in C# that can be null. But value types can’t be null. Don’t worry, you can still use the “as” keyword with value types by changing them to nullable value types. This effectively makes them reference types that can now be null.