{"action":"opened","issue":{"url":"https://api.github.com/repos/dotnet/roslyn/issues/15174","repository_url":"https://api.github.com/repos/dotnet/roslyn","labels_url":"https://api.github.com/repos/dotnet/roslyn/issues/15174/labels{/name}","comments_url":"https://api.github.com/repos/dotnet/roslyn/issues/15174/comments","events_url":"https://api.github.com/repos/dotnet/roslyn/issues/15174/events","html_url":"https://github.com/dotnet/roslyn/issues/15174","id":188818176,"number":15174,"title":"InvalidCastException thrown for converting between types for which explicit/implicit casts are declared","user":{"login":"johndog","id":22213780,"avatar_url":"https://avatars.githubusercontent.com/u/22213780?v=3","gravatar_id":"","url":"https://api.github.com/users/johndog","html_url":"https://github.com/johndog","followers_url":"https://api.github.com/users/johndog/followers","following_url":"https://api.github.com/users/johndog/following{/other_user}","gists_url":"https://api.github.com/users/johndog/gists{/gist_id}","starred_url":"https://api.github.com/users/johndog/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/johndog/subscriptions","organizations_url":"https://api.github.com/users/johndog/orgs","repos_url":"https://api.github.com/users/johndog/repos","events_url":"https://api.github.com/users/johndog/events{/privacy}","received_events_url":"https://api.github.com/users/johndog/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":0,"created_at":"2016-11-11T18:12:08Z","updated_at":"2016-11-11T18:12:08Z","closed_at":null,"body":"I put this here for discussion, I don't know whether there is a good solution.  This isn't a new problem, but I've seen it surface more frequently in the context of generic enumerables and refactoring.\r\n\r\n**Version Used**: 4.5\r\n\r\n**Repro Summary**\r\nThe runtime is unable to cast an object of type 'Foo' to type 'Bar', yielding InvalidCastException, even though there is a conversion path.\r\n\r\nConsider the following code:\r\n\r\n```\r\n        public class Foo\r\n        {\r\n            public Foo(Bar bar)\r\n            {\r\n            }\r\n\r\n            public static implicit operator Foo(Bar bar)\r\n            {\r\n                return new Foo(bar);\r\n            }\r\n            //....\r\n        }\r\n\r\n        public class Bar\r\n        {\r\n            public Bar()\r\n            {\r\n            }\r\n\r\n            public Bar(Foo foo)\r\n            {\r\n            }\r\n\r\n            public static implicit operator Bar(Foo foo)\r\n            {\r\n                return new Bar(foo);\r\n            }\r\n            //....\r\n        }\r\n```\r\n\r\nWith the above code, this operation succeeds:\r\n\r\n     ((Foo)new Bar())\r\n\r\nHowever, both of these fail:\r\n\r\n1. Cast from object:  `((Foo)(object)new Bar())`\r\n2. IEnumerable.Cast<>: `new[] { new Bar() }.Cast<Foo>()`\r\n\r\nThey both issue the same exception:\r\n\r\n     System.InvalidCastException: Unable to cast object of type 'Bar' to type 'Foo'.\r\n\r\nThe second one is especially surprising because, by appearances, there is no intermediate type, and we've already shown that a direct conversion is possible.\r\n\r\n**Problem Details**\r\nThe behavior described here helps create a brittle framework in which types appear to be universally convertible between each other, whereas in reality they are only convertible in one specific manner.  Without the conversion operators, the compiler would error on the direct cast of (Bar)Foo, because the types are unrelated.  The introduction of the conversion operators allows the direct cast to work, and code that takes advantage of this sets the stage for code patterns that allow the two repro cases to sneak in and manifest as runtime exceptions.  Who would look at the successful use of `(Foo)bar` and still think `barArray.Cast<Foo>()` would not work?\r\n\r\nThis is also a problem for refactoring.  For example:  Bar previously inherited from Foo, and the developer has refactored from inheritance to composition, using the conversion operators to smooth the transition, resolving compiler errors.  However, the compiler will not identify all the cases in which casts will no longer function, and lines of code which were never touched in the refactoring will begin throwing **InvalidCastException** at runtime.  Many non-generic collection types still treat elements as type **object**, and while explicit cast would work when the types are related by inheritance, it will not work when they have to be converted via the conversion operator.\r\n\r\n**Hypothetical Solutions**\r\nMy hope would be to drive out as many runtime exceptions as possible, preferring compiler errors instead.  This means either making fewer things work at compile time, or making more things work at runtime.\r\n\r\nWith this in mind, I can think of several plausible improvements:\r\n\r\n1. Enable `((Foo)(object)new Bar())` to work in the above scenario.  This would likely fix the Cast<> scenario as well.  I suppose the runtime would have to have the ability to discover whether the target type of an object had an explicit conversion operator defined for it.  It would also have choose between conversion operators if they exist on both the source and target.  Still, this seems doable; the types are certainly well known when the exception is thrown.  Is it too expensive for the non-exceptional case?  Too difficult to resolve the ambiguity when multiple conversions exist?\r\n2. Drive out the unintended conversion from object to custom types with warnings.  With the surge of generics, it should be less necessary to convert from **object** than in the past.  But apart from the use of Cast<>, I don't know how you can recognize unintended conversions from object, so this path seems very difficult.\r\n3. Deprecate conversion operators.  They are only a partial solution, and therefore a dangerous temptation.  They might be more trouble than they are worth.  If the developer isn't allowed to conflate casting and conversion, they are more likely to use safer patterns in all conversion scenarios.\r\n4. Reimplement Cast<TResult> as  `.Select(_ => (TResult)_)`.  This won't fix the \"cast from object\" case, but it will at least enable Cast<TResult> to work between types that have conversion operators defined, and stop it from always using object as an intermediate step.  This means it will also result in a compiler error when there is no conversion path between TSource and TResult, instead of a runtime exception, which is great.\r\n\r\nOf these, I think the first is the only one that can make refactoring safe.  I haven't thought of a way to use compiler errors to drive out all conversion errors that could be introduced by refactoring.\r\n\r\n\r\n\r\n"}}
{"actor":{"display_login":"johndog"}}