GenericParameter T に何が設定されたか?

LCG 版 Zipper を軽く書いてみようとしたら、これにひっかかった。IEnumerator の T に設定された型を得たい。どうすればいいか?

public class X<T, U> : Y, IEnumerable<U> {}

X<int, string> x = new ...;

例えばこんな場合、x から typeof(string) を得るにはどうすればよいのか?
すぐに思いつくのは、Type.GetGenericArguments() を呼び出すことだけど、ContainsGenericParameters = false で、{ typeof(int), typeof(string) } が返ってきた場合にどうやって IEnumerable のパラメタライズドタイプである typeof(string) を発見するためのインデックス 1 を得ればよいだろうか?
このインデックス値を得るには、Generic 型である typeof(T) や typeof(U) に対して GenericParameterPosition プロパティを参照する必要がある。具体的には、

   x.GetGenericTypeDefinition()                     // X<T, U> 型の
    .GetInterface(typeof(IEnumerable<>).Fullname)   // 実装しているインターフェス IEnumerable<T> の
    .GetGenericArguments()[0]                       // 0 番目のパラメータ (== T) の
    .GenericParameterPosition                       // 保存場所のインデックス値

    // 良い子はエラーチェックとかしようね

となる。型名がわかっているなら、x.GetGenericTypeDefinition() を呼ばずに typeof(X<,>) と記載することもできます。
なお、上記で == T と書いているのは、IEnumerable の T を得るという意味であり、GetGenericArguments()[0].Name の値は X の型定義で IEnumerable`1 へ与えた引数の名称である "U" が得られる。
また、元の型の GetGenericArguments() の結果からインデックスを引くのではなく、直接

x.GetType()
 .GetInterface(typeof(IEnumerable<>).Fullname)
 .GetGenericArguments()[0]

というようにして typeof(string) を得ることも可能になっている。
(最終的にこっちを使うことにした)

また、親クラスにたいして処理をしたい場合には、文中の GetInterface() を BaseType に読み替えるなどして調整してください。