2011-11-22 20 views
22

Sé que hay algunas respuestas en el sitio al respecto y me disculpo si esto se duplica de alguna manera, pero todas las que encontré no hacen lo que intento. hacer.Obtener el nombre de un método utilizando una expresión

Estoy tratando de especificar la información del método para que pueda obtener el nombre de una manera segura al no usar cadenas. Así que estoy tratando de extraerlo con una expresión.

decir que quiero obtener el nombre de un método en esta interfaz:

public interface IMyInteface 
{ 
    void DoSomething(string param1, string param2); 
} 

Actualmente puedo obtener el nombre utilizando este método:

MemberInfo GetMethodInfo<T>(Expression<Action<T>> expression) 
{ 
     return ((MethodCallExpression)expression.Body).Method; 
} 

me puede llamar al método de ayuda de la siguiente :

var methodInfo = GetMethodInfo<IMyInteface>(x => x.DoSomething(null, null)); 
Console.WriteLine(methodInfo.Name); 

Pero estoy buscando la versión que me permita obtener el nombre del método sin especificar los parámetros (null, null)

así:

var methodInfo = GetMethodInfo<IMyInteface>(x => x.DoSomething); 

Pero todos los intentos fallan para compilar

¿Hay una manera de hacer esto?

+0

No tengo VS en este momento, pero creo que debería intentar aceptar 'Delegate' o' Action 'en una alternativa no genérica. En este caso, debería poder pasar un grupo de métodos – Snowbear

+0

Si cambio la entrada a Delegar, no se compilará y el error no podrá convertir el grupo de métodos en delegado. Si lo cambio a Action , parece que funciona pero la expresión se convierte en UnaryExpression y el método es nulo y no puedo ver dónde obtener esto desde el UnaryExpression – Andre

+0

después de convertir ... ¿has probado 'var methodInfo = GetMethodInfo (DoSomething); 'en lugar de' var methodInfo = GetMethodInfo (x => x.DoSomething); '? Además, sigo creyendo que especificar (por defecto (cadena), por defecto (cadena)) es más legible, y puede soportar métodos de sobrecarga en este caso –

Respuesta

11
x => x.DoSomething 

Con el fin de hacer de este compilables sólo veo dos formas:

  1. Ir manera no genérico y especificar que es parámetro como Action<string, string>
  2. especificar Action<string, string> como su target tipo de delegado en solitario: GetMethodInfo<IMyInteface>(x => new Action<string,string>(x.DoSomething))

si estás bien para ir con segundo, lo que le permite omitir los argumentos a continuación, se puede escribir el método de GetMethodInfo de la siguiente manera:

MemberInfo GetMethodInfo<T>(Expression<Func<T, Delegate>> expression) 
    { 
     var unaryExpression = (UnaryExpression) expression.Body; 
     var methodCallExpression = (MethodCallExpression) unaryExpression.Operand; 
     var methodInfoExpression = (ConstantExpression) methodCallExpression.Arguments.Last(); 
     var methodInfo = (MemberInfo) methodInfoExpression.Value; 
     return methodInfo; 
    } 

Funciona para su interfaz, pero probablemente será necesaria una generalización para que funcione con cualquier método, eso depende de usted.

+0

Esperaba algo un poco menos detallado, pero sin embargo, esto funciona bien, gracias. – Andre

+5

Tengo curiosidad, y sé que han pasado ya 2 años, así que espero que todavía puedan responder: ¿Hay alguna forma de permitir una sintaxis como esta: 'GetMethodInfo (x => x.DoSomething);'? – 9ee1

+2

Una nota sobre este enfoque, esto solo funciona en .NET 3.5 y 4.0, no en 4.5. En este último caso, la expresión de llamada al método se representa de manera diferente y, por lo tanto, el orden de los parámetros es diferente. En ese caso, tienes que hacer: 'var methodInfoExpression = (ConstantExpression) methodCallExpression.Object;'. En general, considero que este enfoque es demasiado engorroso. – nawfal

1

Si su aplicación permitiría una dependencia de Moq (o una biblioteca similar), se podría hacer algo como esto:

class Program 
{ 
    static void Main(string[] args) 
    { 
     var methodName = GetMethodName<IMyInteface>(x => new Action<string,string>(x.DoSomething)); 
     Console.WriteLine(methodName); 
    } 

    static string GetMethodName<T>(Func<T, Delegate> func) where T : class 
    { 
     // http://code.google.com/p/moq/ 
     var moq = new Mock<T>(); 
     var del = func.Invoke(moq.Object); 
     return del.Method.Name; 
    } 
} 

public interface IMyInteface 
{ 
    void DoSomething(string param1, string param2); 
} 
+1

-1, esto da el mango al delegado anónimo intermedio que está creando (para pasar al método 'GetMethodInfo'), no al del miembro real que intenta representar (' DoSomething'). – nawfal

+0

@nawful El cartel original pidió el nombre de la cadena, no el MethodInfo original. He actualizado mi ejemplo para aclarar. – Wally

+0

Me preocupa si todavía no da el resultado correcto. ¿Has probado esto? – nawfal

4

El problema con esto es que x.DoSomething representa un grupo de métodos. Y tiene que especificar de forma explícita a qué tipo de delegado desea convertir ese grupo de métodos, para que se pueda seleccionar el miembro correcto del grupo. Y no importa si ese grupo contiene solo un miembro.

El compilador podría inferir que se refiere a eso, pero no hace eso. (Creo que es así para que su código no se rompa si agrega otra sobrecarga de ese método.)

La respuesta de Snowbear contiene buenos consejos sobre posibles soluciones.

+0

+1 por mencionar el problema real .. – nawfal

2

Esta es una nueva respuesta a una pregunta anterior, pero responde a la queja "verbosa" de la respuesta aceptada. Se requiere más código, pero el resultado es una sintaxis como:

MemberInfo info = GetActionInfo<IMyInterface, string, string>(x => x.DoSomething); 

o, para los métodos con un valor de retorno

MemberInfo info = GetFuncInfo<IMyInterface, object, string, string>(x => x.DoSomethingWithReturn); 

donde

object DoSomethingWithReturn(string param1, string param2); 

Al igual que el marco proporciona una acción < > y Func <> delega hasta 16 parámetros, debe tener los métodos GetActionInfo y GetFuncInfo que aceptan hasta 16 parámetros (o más, aunque creo que la refactorización es sabio si tienes métodos con 16 parámetros). Mucho más código, pero una mejora en la sintaxis.

+1

Espero que menos detallado significa que puedo hacer algo como esto: 'GetMethodInfo (x => x.DoSomething);'? Aunque estoy un poco confundido sobre cómo implementar tu sugerencia. ¿Puedes proporcionar una muestra de código rápido? – 9ee1

+0

La única manera de hacerlo (que yo sepa) sin pasar los parámetros es incluir los tipos de parámetros en la especificación genérica - es decir GetMethodInfo (x => x.DoSomething). –

11

El siguiente es compatible con .NET 4.5:

public static string MethodName(LambdaExpression expression) 
{ 
    var unaryExpression = (UnaryExpression)expression.Body; 
    var methodCallExpression = (MethodCallExpression)unaryExpression.Operand; 
    var methodCallObject = (ConstantExpression)methodCallExpression.Object; 
    var methodInfo = (MethodInfo)methodCallObject.Value; 

    return methodInfo.Name; 
} 

Usted puede utilizarlo con expresiones como x => x.DoSomething, sin embargo, requeriría alguna envoltura en métodos genéricos para diferentes tipos de métodos.

Aquí es una versión compatible con versiones anteriores:

private static bool IsNET45 = Type.GetType("System.Reflection.ReflectionContext", false) != null; 

public static string MethodName(LambdaExpression expression) 
{ 
    var unaryExpression = (UnaryExpression)expression.Body; 
    var methodCallExpression = (MethodCallExpression)unaryExpression.Operand; 
    if (IsNET45) 
    { 
     var methodCallObject = (ConstantExpression)methodCallExpression.Object; 
     var methodInfo = (MethodInfo)methodCallObject.Value; 
     return methodInfo.Name; 
    } 
    else 
    { 
     var methodInfoExpression = (ConstantExpression)methodCallExpression.Arguments.Last(); 
     var methodInfo = (MemberInfo)methodInfoExpression.Value; 
     return methodInfo.Name; 
    } 
} 

Comprobar this sample code on Ideone. Tenga en cuenta que Ideone no tiene .NET 4.5.

Cuestiones relacionadas