2011-03-26 15 views
6

Al iniciar sesión, siempre se enreda en literales de cadena.C# pase cualquier método como parámetro

He resuelto que muy bien para propiedades, campos y variables haciendo pasar una Expression<Func<T>> expression (como se explica here), por lo que puede hacer cosas como esta:

public void Demo(string someArgument) 
{ 
    LogFrameWork.LogLine("Demo"); // goal is to get rid of these string literals 
    LogFramework.Log(() => someArgument); 
} 

Quiero hacer algo similar para el método Demo propio :

public void Demo(string someArgument) 
{ 
    LogFramework.Log(this.Demo); 
} 

he intentado cosas como esta:

public static void Log(Delegate method) 
{ 
    string methodName = method.Method.Name; 
    LogLine(methodName); 
} 

y esto:

public static void Log(Action method) 
{ 
    string methodName = method.Method.Name; 
    LogLine(methodName); 
} 

Pero tengo errores de compilación como estos:

Argument 1: cannot convert from 'method group' to 'System.Delegate' 
Argument 1: cannot convert from 'method group' to 'System.Action' 

pude introducir un montón de sobrecargas utilizando Func<...> y Action<...>, pero eso suena demasiado complejo.

¿Hay alguna manera de cubrir esto para cualquier método con cualquier cantidad de parámetros y un resultado opcional?

--jeroen

PS: Creo this question podría tener cierta relevancia aquí, pero no hay respuestas que me consiguió un 'ajá' sentirse :-)

+1

entiendo su pregunta, sin embargo, no cree que sería mucho más fácil y más extensible con algún marco de AOP? Puede crear un atributo personalizado que marque los métodos que deben registrarse en la invocación. – empi

+1

He usado AOP en el pasado, y ha causado que los tiempos de compilación/enlace se disparen. Como .NET ya tiene tiempos de compilación/enlace mucho más altos, prefiero evitar eso. –

+1

Cada solución para este problema general que he visto al usar árboles de expresión ha usado el enfoque de sobrecargas múltiples, que también es un poco incómodo, ya que debe incluir parámetros. Si voy a escribir el registro de seguimiento manual, generalmente solo paso el nombre del método como una constante de cadena y confío en Resharper para recordarme que debo mantenerlos sincronizados. –

Respuesta

3

En lugar de tratar de pasar el método como un parámetro a su registrador, mirarlo desde la perspectiva de tener el registrador de identificar el método de llamada.

He aquí un (pseudo) ejemplo:

Clase Logger

public void Debug(string message) 
{ 
    message = string.Format("{0}: {1}", GetCallingMethodInfo(), message); 
    // logging stuff 
} 

/// <summary> 
/// Gets the application name and method that called the logger. 
/// </summary> 
/// <returns></returns> 
private static string GetCallingMethodInfo() 
{ 
    // we should be looking at the stack 2 frames in the past: 
    // 1. for the calling method in this class 
    // 2. for the calling method that called the method in this class 
    MethodBase method = new StackFrame(2).GetMethod(); 
    string name = method.Name; 
    string type = method.DeclaringType.Name; 

    return string.Format("{0}.{1}", type, name); 
} 

cualquier lugar que utiliza el registrador:

// resides in class Foo 
public void SomeMethod() 
{ 
    logger.Debug("Start"); 
} 

La salida del registrador será entonces: Foo.SomeMethod: Start

+0

1;.. Gracias por la elaboración de la [solución] (http://stackoverflow.com/questions/5443521/c-pass-any-method -as-a-parameter/5443606 # 5443606) que [Josh G] (http://stackoverflow.com/users/64329/josh-g) propuso. Entendí lo que quería decir, pero teniendo a mano un ejemplo más elaborado ayudará mucho a los futuros usuarios. –

+1

Esto es de una biblioteca de registro que escribí usando 'MS Enterprise Logging Library' hace un par de años.Esto solucionó casi todo lo que está intentando lograr, creo, en el sentido de que quiere que el registrador sepa qué método se llama el registrador. Lo bueno de esto es usar 'DeclaringType', terminarás con un NameSpace completo para el método. y nunca tenga que preocuparse de agregar el nombre del método como parte del mensaje de registro. –

+1

Acepté su respuesta porque crea el código más limpio en el sitio de llamada. Sin expresiones complicadas Bonito. –

0

Se puede definir un delegado, luego aceptar que el delegado como un parámetro.

public delegate void DemoDelegate(string arg); 

public void MyMethod(DemoDelegate delegate) 
{ 
    // Call the delegate 
    delegate("some string"); 
} 

Puede llamar MiMetodo así:

MyMethod(delegate(string arg) 
{ 
    // do something 
}); 

o

void MethodThatTakesAString(string value) 
{ 
    // do something 
} 

MyMethod(MethodThatTakesAString); 

ver este enlace para más información:

http://msdn.microsoft.com/en-us/library/aa288459(v=vs.71).aspx

+1

Sé de delegados, pero no veo cómo esto me ayudaría con cualquier método con cualquier cantidad de parámetros. Por favor, elabore, ya que probablemente mire algo muy obvio :-) –

5

También puede lograr esto sin usar ExpressionTrees a través del System.Diagnostics.StackTrace.

StackTrace trace = new StackTrace(); 

Y luego:

trace.GetFrame(0).GetMethod().Name 

Para obtener el MethodInfo y luego el nombre del método actual, o bien:

trace.GetFrame(1).GetMethod().Name 

Para obtener el método de llamada.

+0

+1; ¡Interesante! Investigará esto. Gracias por pensar fuera de la caja. Esto también me ayudará mucho a obtener los parámetros de una manera automágica. –

+1

Esto es especialmente no productivo, y conlleva el riesgo de que se optimicen los marcos. –

+0

@Kirk: se sabe que las expresiones son muy costosas también, ¿se puede elaborar sobre la parte 'optimizado '; ¿te refieres a inline? –

0

Prueba esto:

/// <summary> 
/// Trace data event handler delegate. 
/// </summary> 
/// <returns>The data to write to the trace listeners</returns> 
public delegate object TraceDataEventHandler(); 

public static class Tracing 
{ 

    /// Trace a verbose message using an undefined event identifier and message. 
    /// </summary> 
    /// <param name="message">The delegate to call for the trace message if this event should be traced.</param> 
    [Conditional("TRACE")] 
    public static void TraceVerbose(TraceMessageEventHandler message) 
    { 
     ... your logic here 
    } 
} 

entonces usted puede hacer ...

Tracing.TraceVerbose(() => String.Format(...)); 

Espero haber entendido su pregunta correctamente ... ¿hace esto lo que quiere?

5

Esto es mucho más difícil de lo que parece. Creo que podría ser mejor con las sobrecargas Func y Action genéricas, pero hay una forma de hacerlo con árboles de expresiones. He aquí un ejemplo en LINQPad:

public static void Log(Expression<Action> expr) 
{ 
    Console.WriteLine(((MethodCallExpression)expr.Body).Method.Name); 
} 

void Main() 
{ 
    Log(() => DoIt()); 
    Log(() => DoIt2(null)); 
    Log(() => DoIt3()); 
} 

public void DoIt() 
{ 
    Console.WriteLine ("Do It!"); 
} 

public void DoIt2(string s) 
{ 
    Console.WriteLine ("Do It 2!" + s); 
} 

public int DoIt3() 
{ 
    Console.WriteLine ("Do It 3!"); 
    return 3; 
} 

Este salidas:

DoIt 
DoIt2 
DoIt3

Tenga en cuenta que tuve que usar lambdas y especificar argumentos ficticios al llamar al método de registro.

Esto se basa en Fyodor Soikin's excellent answer.

+0

Gracias por la respuesta; Temía que fuera algo como esto, pero es bueno tenerlo confirmado. –

Cuestiones relacionadas