Rediscovering implicit casting
A lot of my ideas are good, but unfortunately not all of them. On my last blog post, I got feedback (via two reddit posts) that I did not think things through enough. (My wife says that happens more than I realise.) After mulling it over, I realised that the comments that I received are valid and that I need to rectify my mistake.
When I was thinking about ways to make Kotlins type aliases available in C#, I was too focussed on the perceived ease of representing one thing as something else. A
string as a
Uri for example. I did not investigate deeply enough to see that Kotlins type aliases are syntactic sugar to make concepts more explicit and not a real conversion.
This brings me to the first comment and I even put it right into my blog post. 🤦♂️
An implicit cast should always succeed and return a result.
This is stated originally in the Microsoft guidelines:
Predefined C# implicit conversions always succeed and never throw an exception.
Why is it important that there is no exception? Because of the implicit connection between the two types. One type should always be the other type. With an explicit cast, this can be said to be the case sometimes. I'm forcing the cast from one type to another, but it can go wrong. When I'm using an implicit cast, this should be a given.
For example: I can say that a
Uri can always be represented as a
string. I cannot say that every
string should always be a valid
Uri. Especially not if I say that every
string should also be a valid
FileInfo and a valid
DirectoryInfo and a valid
Telephone number. A
string cannot be all at the same time. Yet that is what this implicit cast would indicate.
Therefore, my notion that all their classes should be able to transparently cast from
string to their type is wrong.
A better way to do this would be to write convenience overloads for methods that take a
Uri so it accepts a
Task<T> Data<T>(string location)
// parse string to uri
Task<T> Data<T>(Uri location)
// download data
This makes it easy to pass in a
string instead of a
Uri, yet gives me the option to parse the
string to make sure this is a valid
This brings me to the second point: code should not be magic. If I read the code, I should understand what it is doing, unambiguously. Suddenly going from a
string to a
Uri, without checking, verifying or parsing is not always intuitively. In some cases, it might be, but in a lot more, it won't be.
For as long as I've programmed, I've disliked clever tricks or techniques that look like magic. This should have looked like magic as well, but I was staring too blindly at the Kotlin type alias functionality. This clicked when I read this really good explanation about Visual Basics
To end on a positive note, I think I have found a good place for an implicit cast: inside the builder pattern. A builder should always have a valid instance of the type it has been building. So, casting a builder to the type it's creating is safe, can always be done and does not feel like magic. It works very nicely in my tests where I need to prepare several input objects. For example, an order with several items:
.AddItem("Item 1", price:200, quantity:10)
.AddItem("Item 2", price:500, quantity:2))
public OrderBuilder Create(DateTime orderDate, Person client)
// create order
public OrderBuilder AddItem(string description, int price, int quantity)
// add item to order
public Order Build()
// return order with lines
public static implicit operator Order(OrderBuilder builder) => builder.Build();
Making mistakes is never fun, but I did learn a great deal from it. I learned a lot about casting, I got a history lesson about Visual Basic and I got a lesson in humility. Now I hope others can learn from my mistake as well.