Combining List.ForEach and List.Zip
I agree, the temporary tuples are a code smell. They carry a performance overhead, but worse, they obscure the purpose of the code. Here is a way of achieving the goal by working directly on the enumerators.

private static void TripleForEach&lt;TFirst, TSecond, TThird&gt;(
        IEnumerable&lt;TFirst&gt; first,
        IEnumerable&lt;TSecond&gt; second,
        IEnumerable&lt;TThird&gt; third,
        Action&lt;TFirst, TSecond, TThird&gt; action)
{
    using (IEnumerator&lt;TFirst&gt; e1 = first.GetEnumerator())
    using (IEnumerator&lt;TSecond&gt; e2 = second.GetEnumerator())
    using (IEnumerator&lt;TThird&gt; e3 = third.GetEnumerator())
    {
        while (e1.MoveNext() &amp;&amp; e2.MoveNext() &amp;&amp; e3.MoveNext())
        {
            action(e1.Current, e2.Current, e3.Current);
        }
    }
}