Search code examples
c#castingsyntax-errorigrouping

Why I don't get syntax error in this sample code?


Just trying some basics with C# Lookup collection and found, that this simple example compile fine, even IGrouping interface should have TElement set as int.

string txt = "Hello world!";
ILookup<char, int> occurrences = Enumerable.Range(0, txt.Length).ToLookup(i => txt[i], i => i);

foreach (IGrouping<char, string> values in occurrences)
    Console.WriteLine($"{values.Key}: {string.Join(", ", values)}");

Obviously, I get run-time error on invalid cast. But I expected, that this is work for compiler...


Solution

  • It seems here is the rule that the compiler uses:

    If the compiler can see that the value type or sealed class does not implement or inherit the type the programmer is trying to cast to/from, the cast is illegal at compile-time.

    (Thanks to Jeppe Stig Nielsen for mentioning this in comments.)

    Here is the result of compiling foreach (XXXX values in occurrences) if you use either of below options instead of XXXX in your example. Pay attention occurrences is of type ILookup<char, int> which is derived from IGrouping<char, int>.

    • public class MyClass { } Compiles
    • public sealed class MyClass { } Compile-Time Error
    • public sealed class MyClass : IGrouping<char, int> {...} Compiles
    • public sealed class MyClass : IGrouping<char, string> {...} Compile-Time Error
    • public struct MyStruct {} Compile-Time Error
    • public struct MyStruct : IGrouping<char, string> {...} Compile-Time Error
    • public struct MyStruct : IGrouping<char, int> {...} Compiles

    Here is a good blog post by Krzysztof Cwalina that describes how Duck typing in used in foreach:

    C#’s foreach operator already uses duck typing. This might be surprising to some, but to support foreach in C# you don’t need to implement IEnumerable! All you have to do is:

    Provide a public method GetEnumerator that takes no parameters and returns a type that has two members: a) a method MoveMext that takes no parameters and return a Boolean, and b) a property Current with a getter that returns an Object.