with what method done create c# reflection

c# - what - Seleccione el método genérico correcto con la reflexión



system.reflection c# (11)

Quiero seleccionar el método genérico correcto a través de la reflexión y luego llamarlo.

Usualmente esto es bastante fácil. Por ejemplo

var method = typeof(MyType).GetMethod("TheMethod"); var typedMethod = method.MakeGenericMethod(theTypeToInstantiate);

Sin embargo, el problema comienza cuando hay diferentes sobrecargas genéricas del método. Por ejemplo, los métodos estáticos en System.Linq.Queryable-class. Hay dos definiciones del método ''Dónde''

static IQueryable<T> Where(this IQueryable<T> source, Expression<Func<T,bool>> predicate) static IQueryable<T> Where(this IQueryable<T> source, Expression<Func<T,int,bool>> predicate)

Este meand que GetMethod no funciona, porque no puede destinar los dos. Por lo tanto, quiero seleccionar el correcto.

Hasta ahora, a menudo solo tomaba el primero o el segundo método, según mi necesidad. Me gusta esto:

var method = typeof (Queryable).GetMethods().First(m => m.Name == "Where"); var typedMethod = method.MakeGenericMethod(theTypeToInstantiate);

Sin embargo, no estoy contento con esto, porque asumo que el primer método es el correcto. Prefiero encontrar el método correcto por tipo de argumento. Pero no pude entender cómo.

Lo intenté con pasar los ''tipos'', pero no funcionó.

var method = typeof (Queryable).GetMethod( "Where", BindingFlags.Static, null, new Type[] {typeof (IQueryable<T>), typeof (Expression<Func<T, bool>>)}, null);

Entonces, ¿alguien tiene una idea de cómo puedo encontrar el método genérico ''correcto'' a través de la reflexión? Por ejemplo, ¿la versión correcta del método ''Where'' en la clase Queryable?


Además de la respuesta de @ MBoros.

Puede evitar escribir argumentos genéricos complejos utilizando este método de ayuda:

public static MethodInfo GetMethodByExpression<Tin, Tout>(Expression<Func<IQueryable<Tin>, IQueryable<Tout>>> expr) { return (expr.Body as MethodCallExpression).Method; }

Uso:

var where = GetMethodByExpression<int, int>(q => q.Where((x, idx) => x > 2));

O

var select = GetMethodByExpression<Person, string>(q => q.Select(x => x.Name));


Deje que el compilador lo haga por usted:

var fakeExp = (Expression<Func<IQueryable<int>, IQueryable<int>>>)(q => q.Where((x, idx) => x> 2)); var mi = ((MethodCallExpression)fakeExp.Body).Method.GetGenericMethodDefinition();

para el índice Where with, o simplemente omita el segundo parámetro en la expresión Where para el sin


Descubrí la forma más fácil de usar expresiones de iQuerable al llamar al método mediante la reflexión. Por favor vea el siguiente código:

Puede usar la expresión IQuerable según los requisitos.

var attributeName = "CarName"; var attributeValue = "Honda Accord"; carList.FirstOrDefault(e => e.GetType().GetProperty(attributeName).GetValue(e, null) as string== attributeValue);


Esta pregunta tiene alrededor de 2 años, pero se me ocurrió (creo que es) una solución elegante, y pensé que la compartiría con los buenos aquí en . Es de esperar que ayude a quienes llegan aquí a través de varias consultas de búsqueda.

El problema, como decía el cartel, es obtener el método genérico correcto. Por ejemplo, un método de extensión LINQ puede tener toneladas de sobrecargas, con argumentos de tipo anidados dentro de otros tipos genéricos, todos utilizados como parámetros. Yo quería hacer algo como esto:

var where = typeof(Enumerable).GetMethod( "Where", typeof(IQueryable<Refl.T1>), typeof(Expression<Func<Refl.T1, bool>> ); var group = typeof(Enumerable).GetMethod( "GroupBy", typeof(IQueryable<Refl.T1>), typeof(Expression<Func<Refl.T1, Refl.T2>> );

Como puede ver, he creado algunos tipos de stub "T1" y "T2", clases anidadas dentro de una clase "Refl" (una clase estática que contiene todas mis diversas funciones de extensión de la utilidad Reflection, etc. Sirven como marcadores de posición para donde los parámetros de tipo habrían sido normalmente. Los ejemplos anteriores corresponden a obtener los siguientes métodos LINQ, respectivamente:

Enumerable.Where(IQueryable<TSource> source, Func<TSource, bool> predicate); Enumerable.GroupBy(IQueryable<Source> source, Func<TSource, TKey> selector);

Por lo tanto, debe quedar claro que Refl.T1 va a donde se TSource ido, en ambas llamadas; y el Refl.T2 representa el parámetro TKey . Las clases TX se declaran como tales:

static class Refl { public sealed class T1 { } public sealed class T2 { } public sealed class T3 { } // ... more, if you so desire. }

Con tres clases de TX , su código puede identificar métodos que contienen hasta tres parámetros de tipo genérico.

La siguiente parte de la magia es implementar la función que realiza la búsqueda a través de GetMethods() :

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<Type, Type>(); var args = method.GetGenericArguments(); if (args.Length >= 1) d[typeof(T1)] = args[0]; if (args.Length >= 2) d[typeof(T2)] = args[1]; if (args.Length >= 3) d[typeof (T3)] = args[2]; if (args.Length > 3) throw new NotSupportedException("Too many type parameters."); var p = method.GetParameters(); for (var i = 0; i < 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 < p.Length; i++) { var pt = parameters[i]; if (pt != p[i].ParameterType) { correct = false; break; } } if (correct) return method; } } return null; }

El código anterior hace la mayor parte del trabajo: itera a través de todos los métodos en un tipo particular y los compara con los tipos de parámetros dados para buscar. ¡Pero espera! ¿Qué hay de esa función "sustituta"? Esa es una pequeña y agradable función recursiva que buscará en todo el árbol de tipos de parámetros; después de todo, un tipo de parámetro puede ser un tipo genérico, que puede contener tipos Refl.TX , que deben intercambiarse por los parámetros de tipo "real" que están escondidos de nosotros

private static Type Substitute(Type t, IDictionary<Type, Type> 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 < 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; }


Hice una pequeña función de ayudante:

Func<Type, string, Type[], Type[], MethodInfo> getMethod = (t, n, genargs, args) => { var methods = from m in t.GetMethods() where m.Name == n && m.GetGenericArguments().Length == genargs.Length let mg = m.IsGenericMethodDefinition ? m.MakeGenericMethod(genargs) : m where mg.GetParameters().Select(p => p.ParameterType).SequenceEqual(args) select mg ; return methods.Single(); };

Funciona para productos no genéricos simples:

var m_movenext = getMethod(typeof(IEnumerator), nameof(IEnumerator.MoveNext), Type.EmptyTypes, Type.EmptyTypes);

Como para los genéricos complicados:

var t_source = typeof(fillin1); var t_target = typeof(fillin2); var m_SelectMany = getMethod( typeof(Enumerable), nameof(Enumerable.SelectMany), new[] { t_source, t_target }, new[] { typeof(IEnumerable<>).MakeGenericType(t_source), typeof(Func<,>).MakeGenericType(t_source, typeof(IEnumerable<>).MakeGenericType(t_target)) });


La respuesta de Antamir fue muy útil para mí, pero tiene un error porque no valida que el número de parámetros encontrados en el método coincida con la cantidad de tipos pasados ​​cuando se proporciona una combinación de tipos genéricos y concretos.

Por ejemplo, si ejecutó:

type.GetMethod("MyMethod",typeof(Refl.T1),typeof(bool))

no puede diferenciar entre dos métodos:

MyMethod<T>(T arg1) MyMethod<T>(T arg1, bool arg2)

Las dos llamadas a:

var p = method.GetParameters();

debe cambiarse a:

var p = method.GetParameters(); if (p.Length != parameters.Length) { correct = false; continue; }

Además, las dos líneas de ''ruptura'' existentes deben ser ''continuar''.


La respuesta de Chris Moschini es buena cuando conoces el nombre del método en tiempo de compilación. La respuesta de Antamir funciona si obtenemos el nombre del método en tiempo de ejecución, pero es bastante exagerado.

Estoy usando otra forma, para lo cual obtuve inspiración utilizando el reflector de la función .NET Expression.Call , que selecciona el método genérico correcto de una cadena.

public static MethodInfo GetGenericMethod(Type declaringType, string methodName, Type[] typeArgs, params Type[] argTypes) { foreach (var m in from m in declaringType.GetMethods() where m.Name == methodName && typeArgs.Length == m.GetGenericArguments().Length && argTypes.Length == m.GetParameters().Length select m.MakeGenericMethod(typeArgs)) { if (m.GetParameters().Select((p, i) => p.ParameterType == argTypes[i]).All(x => x == true)) return m; } return null; }

Uso:

var m = ReflectionUtils.GetGenericMethod(typeof(Queryable), "Where", new[] { typeof(Person) }, typeof(IQueryable<Person>), typeof(Expression<Func<Person, bool>>));

Si solo necesita una definición de método genérico o simplemente no conoce el tipo T en ese momento, puede usar algunos tipos falsos y luego quitar la información del genérico:

var m = ReflectionUtils.GetGenericMethod(typeof(Queryable), "Where", new[] { typeof(object) }, typeof(IQueryable<object>), typeof(Expression<Func<object, bool>>)); m = m.GetGenericMethodDefinition();


Otra solución que puede MethodInfo útil: es posible obtener un MethodInfo basado en Expression.Call que ya tiene una lógica para la resolución de sobrecarga.

Por ejemplo, en caso de que necesite obtener algún método específico de Enumerable.Where que podría lograrse utilizando el siguiente código:

var mi = Expression.Call(typeof (Enumerable), "Where", new Type[] {typeof (int)}, Expression.Default(typeof (IEnumerable<int>)), Expression.Default(typeof (Func<int, int, bool>))).Method;

Tercer argumento en el ejemplo: describe tipos de argumentos genéricos y todos los demás argumentos: tipos de parámetros.

De la misma manera, es posible obtener incluso typeof (YourClass) genéricos de objetos no estáticos. Solo debe cambiar el primer argumento de typeof (YourClass) a Expression.Default(typeof (YourClass)) .

En realidad, he usado ese enfoque en mi plugin para .NET Reflection API. Puede verificar cómo funciona here


Puede seleccionar de manera un tanto elegante una sobrecarga genérica específica de un método en tiempo de compilación, sin pasar ninguna cadena a búsquedas en tiempo de ejecución como las otras respuestas aquí.

Métodos estáticos

Supongamos que tiene múltiples métodos estáticos con el mismo nombre, como:

public static void DoSomething<TModel>(TModel model) public static void DoSomething<TViewModel, TModel>(TViewModel viewModel, TModel model) // etc

Si crea una Acción o Func que coincida con el conteo genérico y el recuento de parámetros de la sobrecarga que está buscando, puede seleccionarlo en tiempo de compilación con relativamente pocas acrobacias.

Ejemplo: seleccione el primer método - devuelve vacío, entonces use una Acción, toma una genérica. Usamos Object para evitar especificar el tipo por el momento:

var method = new Action<object>(MyClass.DoSomething<object>);

Ejemplo: Seleccione el segundo método - devuelve vacío, entonces Acción, 2 tipos genéricos, entonces use el tipo de objeto dos veces, una para cada uno de los 2 parámetros genéricos:

var method = new Action<object, object>(MyClass.DoSomething<object, object>);

Acabas de obtener el método que querías sin tener que hacer una plétora loca, y sin buscar en tiempo de ejecución ni utilizar cadenas de caracteres arriesgadas.

MethodInfo

Normalmente, en Reflection desea el objeto MethodInfo, que también puede obtener de forma segura para la compilación. Esto sucede cuando pasas los tipos genéricos reales que deseas usar en tu método. Asumiendo que quería el segundo método anterior:

var methodInfo = method.Method.MakeGenericMethod(type1, type2);

Existe su método genérico sin ninguna de las búsquedas de reflexión o llamadas a GetMethod (), o cadenas endebles.

Métodos de extensión estática

El ejemplo específico que cites con Queryable.Where overloads te obliga a tener un poco de fantasía en la definición de Func, pero en general sigue el mismo patrón. La firma del método de extensión Where () más utilizado es:

public static IQueryable<TModel> Where<TModel>(this IQueryable<TModel>, Expression<Func<TModel, bool>>)

Obviamente, esto será un poco más complicado, aquí está:

var method = new Func<IQueryable<object>, Expression<Func<object, bool>>, IQueryable<object>>(Queryable.Where<object>); var methodInfo = method.Method.MakeGenericMethod(modelType);

Métodos de instancia

Incorporando el comentario de Valerie: para obtener un método de instancia, tendrás que hacer algo muy similar. Supongamos que tiene este método de instancia en su clase:

public void MyMethod<T1>(T1 thing)

Primero seleccione el método de la misma manera que para estática:

var method = new Action<object>(MyMethod<object>);

Luego llame a GetGenericMethodDefinition() para obtener MethodInfo genérico, y finalmente pase su (s) tipo (s) con MakeGenericMethod() :

var methodInfo = method.Method.GetGenericMethodDefinition().MakeGenericMethod(type1);

Desacoplamiento MethodInfo y tipos de parámetros

Esto no se solicitó en la pregunta, pero una vez que haga lo anterior, puede encontrar que selecciona el método en un lugar y decide qué tipos pasarlo en otro. Puedes desacoplar esos 2 pasos.

Si no está seguro de los parámetros de tipo genérico que va a pasar, siempre puede adquirir el objeto MethodInfo sin ellos.

Estático:

var methodInfo = method.Method;

Ejemplo:

var methodInfo = method.Method.GetGenericMethodDefinition();

Y pásalo a otro método que conozca los tipos con los que quiere crear instancias y llame al método, por ejemplo:

processCollection(methodInfo, type2); ... protected void processCollection(MethodInfo method, Type type2) { var type1 = typeof(MyDataClass); object output = method.MakeGenericMethod(type1, type2).Invoke(null, new object[] { collection }); }

Una cosa que ayuda especialmente con esto es seleccionar un método de instancia específico de una clase, desde el interior de la clase, y luego exponerlo a las personas externas que lo necesiten con varios tipos más adelante.

Ediciones: Explicaciones limpias, incorporó el ejemplo del método de instancia de Valerie.


Se puede hacer, ¡pero no es bonito!

Por ejemplo, para obtener la primera sobrecarga de Where menciona en su pregunta, puede hacer esto:

var where1 = typeof(Queryable).GetMethods() .Where(x => x.Name == "Where") .Select(x => new { M = x, P = x.GetParameters() }) .Where(x => x.P.Length == 2 && x.P[0].ParameterType.IsGenericType && x.P[0].ParameterType.GetGenericTypeDefinition() == typeof(IQueryable<>) && x.P[1].ParameterType.IsGenericType && x.P[1].ParameterType.GetGenericTypeDefinition() == typeof(Expression<>)) .Select(x => new { x.M, A = x.P[1].ParameterType.GetGenericArguments() }) .Where(x => x.A[0].IsGenericType && x.A[0].GetGenericTypeDefinition() == typeof(Func<,>)) .Select(x => new { x.M, A = x.A[0].GetGenericArguments() }) .Where(x => x.A[0].IsGenericParameter && x.A[1] == typeof(bool)) .Select(x => x.M) .SingleOrDefault();

O si querías la segunda sobrecarga:

var where2 = typeof(Queryable).GetMethods() .Where(x => x.Name == "Where") .Select(x => new { M = x, P = x.GetParameters() }) .Where(x => x.P.Length == 2 && x.P[0].ParameterType.IsGenericType && x.P[0].ParameterType.GetGenericTypeDefinition() == typeof(IQueryable<>) && x.P[1].ParameterType.IsGenericType && x.P[1].ParameterType.GetGenericTypeDefinition() == typeof(Expression<>)) .Select(x => new { x.M, A = x.P[1].ParameterType.GetGenericArguments() }) .Where(x => x.A[0].IsGenericType && x.A[0].GetGenericTypeDefinition() == typeof(Func<,,>)) .Select(x => new { x.M, A = x.A[0].GetGenericArguments() }) .Where(x => x.A[0].IsGenericParameter && x.A[1] == typeof(int) && x.A[2] == typeof(bool)) .Select(x => x.M) .SingleOrDefault();