MatchCollectionGroupCollectionCaptureCollectionの各コレクションは、当初非ジェネリックなIEnumerableインターフェイスのみを実装する型指定されていないコレクションとして登場し、その後.NET Standard 2.1/.NET Core 2.0にてIEnumerable<T>/ICollection<T>/IList<T>を実装するようになり、型指定されたコレクションとして扱うことができるようになっています。

一方、Match/Group/CaptureCollectionのGetEnumeratorメソッドは、おそらくAPI互換性維持の観点から、IEnumerable<T>の実装以降も非ジェネリックなIEnumeratorを返す実装のままとなっています。 このためC#では、これらのコレクションクラスから取得した列挙子IEnumeratorで列挙する場合、varによる暗黙的な型指定を行うとobjectとして列挙されます

したがって、これらのコレクションをforeachで列挙を行う際には、明示的に型指定する、IEnumerable<Match>等にキャストしてから列挙する、LINQのOfType<Match>()メソッドCast<Match>()メソッドを用いて型変換してから列挙する、などする必要があります。

Match/Group/CaptureCollectionから直接ではなく、ジェネリックインターフェイスを介してGetEnumeratorメソッドを呼び出す場合は、ジェネリックな(型指定された)列挙子IEnumerator<T>を取得できます。 そのため、これらのコレクションを引数としてとる場合は、Match/Group/CaptureCollectionの代わりにIEnumerable/IReadOnlyCollection/IReadOnlyList<Match/Group/Capture>などで受けるようにすることもできます。

Match/Group/CaptureCollectionをIReadOnlyList<Match/Group/Capture>インターフェイスを介して列挙する .NET Standard 2.1/.NET Core 2.0
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;

class Sample {
  // MatchCollection直接ではなくIReadOnlyList<Match>として引き受ける
  static void PrintMatches(IReadOnlyList<Match> matches)
  {
    // IReadOnlyList<Match>を介して列挙することで、型指定されたIEnumerator<Match>によって列挙することができる
    foreach (var match in matches) {
      Console.WriteLine(match.Value);

      PrintGroups(match.Groups);
    }
  }

  // GroupCollectionの場合もMatchCollectionと同様
  static void PrintGroups(IReadOnlyList<Group> groups)
  {
    foreach (var group in groups) {
      Console.WriteLine(group.Value);

      PrintCaptures(group.Captures);
    }
  }

  // CaptureCollectionの場合もMatchCollectionと同様
  static void PrintCaptures(IReadOnlyList<Capture> captures)
  {
    foreach (var capture in captures) {
      Console.WriteLine(capture.Value);
    }
  }

  static void Main()
  {
    PrintMatches(Regex.Matches("text", "pattern"));
  }
}

IEnumerable・IEnumeratorインターフェイスと列挙について詳しくはIEnumerable・IEnumeratorを参照してください。