none
Theoretically, I believe it should be possible to cast a IEnumerable to IEnumerable<TSource> but I Get 'InvalidCastException', Why? RRS feed

  • Question

  • I get the following error message:

    Unhandled Exception: System.InvalidCastException: Unable to cast object of type 'System.Text.RegularExpressions.MatchCollection' to type 'System.Collections.Generic.IEnumerable`1[System.Text.RegularExpressions.Match]'.

    in my code when I try to point to the 'MatchCollection with a generic IEnumerable<out T>.

    So:

    System.String input;

    System.Text.RegularExpressions.Regex regex =

    new System.Text.RegularExpressions.Regex("social");

    System.Text.RegularExpressions.MatchCollection matches = regex.Matches(input); System.Collections.Generic.IEnumerable<System.Text.RegularExpressions.Match> myEnumerableMatches = matches;


    So, I've been told that System.Text.RegularExpressions.MatchCollection does not implement System.Collections.Generic.IEnumerable<out T> or (`1), however you want to write that. Which is a fine explanation, I guess. But my question is, more theoretical and ask the question, "Well, why does it have to?"

    If the MatchCollection can be enumerated using a regular System.Linq.IEnumerable which returns each Match in the MatchCollection as an untyped object, then you have all you really need to use the object. You have the address in memory where the object data is stored. You have the address of the object in memory where it begins. And if you as the programmer know the type of the objects in the collection, why can't you use a System.Collections.Generic.IEnumerable<T> to enumerate the System.Linq.IEnumerable that returns plain old objects, yourself?

    To say it another way, it seems that a foreach loop has no trouble at all enumerating the MatchCollection because it's performing a cast on each object before it uses it. For example:

    foreach(System.Text.RegularExpressions.Match m in matches) { // do whatever here };

    But I can't do this:

    System.Text.RegularExpressions.MatchCollection matches = regex.Matches(input); System.Collections.Generic.IEnumerable<System.Text.RegularExpressions.Match> myEnumerableMatches = matches;

    myEnumerableMatches = matches;


    The IEnumerable of type <Match> know's the types in the collection it is pointing to because I've told it. Why is this a problem?


    Friday, November 16, 2018 3:48 PM

All replies

  • As you can see here, MatchCollection is a rather old class that only implements ICollection interface but not IEnumerable<T>.

    You can't cast object which itself and it's base types haven't implement an interface to that interface. The type for return value of .GetEnumerator() will not be IEnumerator<T>.

    The whole point of Generics is to eliminate the cost of boxing/unboxing. If it would require casting to work, you ought to go back and use IEnumerable instead and save all the trouble.


    Monday, November 19, 2018 1:21 AM
    Answerer
  • Hi 7h3D4rkKn1gh7,

    Please check the document about  MatchCollection that mentioned.

    To iterate through the members of the collection, you should use the collection iteration construct provided by your language (such as foreach in C# and For Each…Next in Visual Basic) instead of retrieving the enumerator that is returned by the GetEnumerator method.

    https://docs.microsoft.com/en-us/dotnet/api/system.text.regularexpressions.matchcollection?redirectedfrom=MSDN&view=netframework-4.7.2

    In addition, you could write custom extension method to achieve it. like this:

     public static class ExtensionMethods
        {
            /// <summary>
            /// Returns the input typed as a generic IEnumerable of the groups
            /// </summary>
            /// <param name="m"></param>
            /// <returns></returns>
            public static IEnumerable<System.Text.RegularExpressions.Group> AsEnumerable(this System.Text.RegularExpressions.GroupCollection gc)
            {
                foreach (System.Text.RegularExpressions.Group g in gc)
                {
                    yield return g;
                }
            }
            /// <summary>
            /// Returns the input typed as a generic IEnumerable of the matches
            /// </summary>
            /// <param name="mc"></param>
            /// <returns></returns>
            public static IEnumerable<System.Text.RegularExpressions.Match> AsEnumerable(this System.Text.RegularExpressions.MatchCollection mc)
            {
                foreach (System.Text.RegularExpressions.Match m in mc)
                {
                    yield return m;
                }
            }
        }

    #Usage:

    System.Collections.Generic.IEnumerable<System.Text.RegularExpressions.Match> myEnumerableMatches =
    
                        matches.AsEnumerable();

    Best regards,

    Zhanglong 


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Monday, November 19, 2018 1:47 AM
    Moderator