A friend asked me about some issues he was having using Enumerable.Cast<T>(). In his mind, it just wasn’t working. Like so many problems, it was working correctly, just not the way he expected. It’s worth examining.
Examine this class:
Note: I normally recommend against conversions, (See Item 28 in Effective C#), but that’s the key to this issue.
Consider this code (assume that GetSomeSttrings() returns a sequence of strings)
You’d expect that GetSomeStrings().Cast<MyType>() would correctly convert each string to a MyType usingthe implicit conversion operator defined in MyType. It doesn’t, it throws an InvalidCastException.
The above code is equivalent to this construct, using a query expression:
The type declaration on the range variable is converted to a call to Cast<MyType> by the compiler (See Item 36 in More Effective C#). Again, it throws an InvalidCastException.
Here’s one way to restructure the code so that it works:
What’s the difference? The two versions that don’t work use Cast<T>(), and the version that works includes the cast in the lambda used as the argument to Select().
And, that’s where the difference lies.
When the compiler creates IL for Cast<T>, it can only assume the functionality in System.Object. System.Object does not contain any conversion methods, therefore, Cast<T>() does not generate any IL that might call any conversion operators.
Cast<T>() will only succeed if its argument is not derived from the target (or a type that implements the target if the target is an interface), Cast<T> fails.
On the other hand, placing the cast in the lambda for the Select clause enables the compiler to know about the conversion operators in the MyType class. That means in succeeds.
As I’ve pointed out before, I normally view Conversion operators as a code smell. On occasion, they are useful, but often they’ll cause more problems than they are worth. Here, without the conversion operators, no developer would be tempted to write the example code that didn’t work.
Of course, if I’m recommending not to use conversion operators, I should offer an alternative. MyType already contains a read/write property to store the string property, so you can just remove the conversion operators and write either of these constructs:
Also, if you needed to, you could create a different constructor for MyType.
All of these projects are Open Source (using the Creative Commons license for content, and the MIT license for code). If you would like to contribute, visit our GitHub Repository. Or, if you have questions, comments, or ideas for improvement, please create an issue for us.