Pair 라는 식별자가 무엇을 의미하는 지 명확하지 않기 때문에, 튜플로 정의하는 게 낫지 않을까 생각됩니다.
또한, 개인적으로는 foreach가 절차형 코드라, 확장 메서드를 아래와 같이 정의할 것 같습니다.
static class IEnumerableExtensions
{
public static IEnumerable<TResult> While<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, (bool matching, TResult whenMatched)> check) =>
source.Select(check)
.Where(t => t.matching)
.Select(t => t.whenMatched);
}
string?[] numbers = ["1", "", null, "2", "four"];
IEnumerable<int> ints = numbers.While(x => (int.TryParse(x, out int n), n));
또한, SelectWhen.Select 는 단순 Select만 다루는데, First, Last, TakeWhile, SkipWhile 도 커버하는 구현도 필요할 것 같습니다.
참고로, 동일하게 IQueryable<T> 도 아래와 같이 정의할 수 있겠지만,
static class IQueryableExtensions
{
public static IQueryable<TResult> While<TSource, TResult>(
this IQueryable<TSource> source,
Expression<Func<TSource, (bool matching, TResult whenMatched)>> check) =>
source.Select(check)
.Where(t => t.matching)
.Select(t => t.whenMatched);
}
실제로 동작 가능한지 - IQueryable 구현자(대표적으로 EF core 제공자)들이 문제 없이 처리하는지는 확인이 필요할 것 같습니다. (int.Parse 는 대부분 지원할 것으로 예상하지만, int.TryParse는 모르겠네요.)