Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>This question is about 2 years old, but I came up with (what I think is) an elegant solution, and thought I'd share it with the fine folks here at StackOverflow. Hopefully it will help those who arrive here via various search queries. </p> <p>The problem, as the poster stated, is to get the correct generic method. For example, a LINQ extension method may have tons of overloads, with type arguments nested inside other generic types, all used as parameters. I wanted to do something like this:</p> <pre><code>var where = typeof(Enumerable).GetMethod( "Where", typeof(IQueryable&lt;Refl.T1&gt;), typeof(Expression&lt;Func&lt;Refl.T1, bool&gt;&gt; ); var group = typeof(Enumerable).GetMethod( "GroupBy", typeof(IQueryable&lt;Refl.T1&gt;), typeof(Expression&lt;Func&lt;Refl.T1, Refl.T2&gt;&gt; ); </code></pre> <p>As you can see, I've created some stub types "T1" and "T2", nested classes within a class "Refl" (a static class which contains all my various Reflection utility extension functions, etc. They serve as placeholders for where the type parameters would have normally went. The examples above correspond to getting the following LINQ methods, respectively:</p> <pre><code>Enumerable.Where(IQueryable&lt;TSource&gt; source, Func&lt;TSource, bool&gt; predicate); Enumerable.GroupBy(IQueryable&lt;Source&gt; source, Func&lt;TSource, TKey&gt; selector); </code></pre> <p>So it should be clear that <code>Refl.T1</code> goes where <code>TSource</code> would gone, in both of those calls; and the <code>Refl.T2</code> represents the <code>TKey</code> parameter.The <code>TX</code> classes are declared as such:</p> <pre><code>static class Refl { public sealed class T1 { } public sealed class T2 { } public sealed class T3 { } // ... more, if you so desire. } </code></pre> <p>With three <code>TX</code> classes, your code can identify methods containing up to three generic type parameters. </p> <p>The next bit of magic is to implement the function that does the search via <code>GetMethods()</code>:</p> <pre><code>public static MethodInfo GetMethod(this Type t, string name, params Type[] parameters) { foreach (var method in t.GetMethods()) { // easiest case: the name doesn't match! if (method.Name != name) continue; // set a flag here, which will eventually be false if the method isn't a match. var correct = true; if (method.IsGenericMethodDefinition) { // map the "private" Type objects which are the type parameters to // my public "Tx" classes... var d = new Dictionary&lt;Type, Type&gt;(); var args = method.GetGenericArguments(); if (args.Length &gt;= 1) d[typeof(T1)] = args[0]; if (args.Length &gt;= 2) d[typeof(T2)] = args[1]; if (args.Length &gt;= 3) d[typeof (T3)] = args[2]; if (args.Length &gt; 3) throw new NotSupportedException("Too many type parameters."); var p = method.GetParameters(); for (var i = 0; i &lt; p.Length; i++) { // Find the Refl.TX classes and replace them with the // actual type parameters. var pt = Substitute(parameters[i], d); // Then it's a simple equality check on two Type instances. if (pt != p[i].ParameterType) { correct = false; break; } } if (correct) return method; } else { var p = method.GetParameters(); for (var i = 0; i &lt; p.Length; i++) { var pt = parameters[i]; if (pt != p[i].ParameterType) { correct = false; break; } } if (correct) return method; } } return null; } </code></pre> <p>The code above does the bulk of the work -- it iterates through all the Methods in a particular type, and compares them to the given parameter types to search for. But wait! What about that "substitute" function? That's a nice little recursive function that will search through the entire parameter type tree -- after all, a parameter type can itself be a generic type, which may contain <code>Refl.TX</code> types, which have to be swapped for the "real" type parameters which are hidden from us.</p> <pre><code>private static Type Substitute(Type t, IDictionary&lt;Type, Type&gt; env ) { // We only really do something if the type // passed in is a (constructed) generic type. if (t.IsGenericType) { var targs = t.GetGenericArguments(); for(int i = 0; i &lt; targs.Length; i++) targs[i] = Substitute(targs[i], env); // recursive call t = t.GetGenericTypeDefinition(); t = t.MakeGenericType(targs); } // see if the type is in the environment and sub if it is. return env.ContainsKey(t) ? env[t] : t; } </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload