2010-03-08 8 views
39

me gustaría hacer algo como esto:¿Puedo obtener los nombres/valores de los parámetros de forma procedimental de la función que se está ejecutando actualmente?

public MyFunction(int integerParameter, string stringParameter){ 
    //Do this: 
    LogParameters(); 
    //Instead of this: 
    //Log.Debug("integerParameter: " + integerParameter + 
    //   ", stringParameter: " + stringParameter); 

} 

public LogParameters(){ 
    //Look up 1 level in the call stack (if possible), 
    //Programmatically loop through the function's parameters/values 
    //and log them to a file (with the function name as well). 
    //If I can pass a MethodInfo instead of analyzing the call stack, great. 
} 

Ni siquiera estoy seguro de lo que quiero hacer es posible, pero sería muy agradable para poder automáticamente la salida de los nombres de parámetros/valores en tiempo de ejecución a un archivo sin escribir explícitamente el código para registrarlos.

¿Es posible?

+2

próxima al duplicado de http://stackoverflow.com/questions/135782/generic-logging-of-function-parameters-in-exception-handl ing –

+0

... y esto http://stackoverflow.com/questions/819576/is-it-possible-to-get-parameters-values-for-each-frame-in-call-stack-in-net –

+0

Dupe de http://stackoverflow.com/questions/1820630/how-to-get-parameter-value-from-stacktrace –

Respuesta

21

realizo personas vinculadas a otras preguntas que mencionaban PostSharp, pero no pude dejar de publicar el código que resuelve mi problema (utilizando PostSharp) para que otras personas podrían beneficiarse de eso.

class Program { 
    static void Main(string[] args) { 
     Trace.Listeners.Add(new TextWriterTraceListener(Console.Out)); 
     new MyClass().MyMethod(44, "asdf qwer 1234", 3.14f, true); 
     Console.ReadKey(); 
    } 
} 
public class MyClass { 
    public MyClass() { 
    } 
    [Trace("Debug")] 
    public int MyMethod(int x, string someString, float anotherFloat, bool theBool) { 
     return x + 1; 
    } 
} 
[Serializable] 
public sealed class TraceAttribute : OnMethodBoundaryAspect { 
    private readonly string category; 

    public TraceAttribute(string category) { 
     this.category = category; 
    } 

    public string Category { get { return category; } } 

    public override void OnEntry(MethodExecutionArgs args) { 
     Trace.WriteLine(string.Format("Entering {0}.{1}.", 
             args.Method.DeclaringType.Name, 
             args.Method.Name), category); 

     for (int x = 0; x < args.Arguments.Count; x++) { 
      Trace.WriteLine(args.Method.GetParameters()[x].Name + " = " + 
          args.Arguments.GetArgument(x)); 
     } 
    } 

    public override void OnExit(MethodExecutionArgs args) { 
     Trace.WriteLine("Return Value: " + args.ReturnValue); 

     Trace.WriteLine(string.Format("Leaving {0}.{1}.", 
             args.Method.DeclaringType.Name, 
             args.Method.Name), category); 
    } 
} 

Simplemente añadiendo el atributo Trace a un método hará que muy agradable información de depuración que debe emitirse, así:

Debug: Entering MyClass.MyMethod. 
x = 44 
someString = asdf qwer 1234 
anotherFloat = 3.14 
theBool = True 
Return Value: 45 
Debug: Leaving MyClass.MyMethod. 
+3

¿es esto posible con la versión expresa de postsharp (que es gratuita)? – Dbl

12

Teóricamente es posible con una creación y optimización de depuración desactivadas, pero, en términos prácticos, sugiero que desee pasar el código de reescritura.

La gente va a seguir diciéndote que la reflexión funcionará cuando no, así que here is the function that's actually capable of getting argument values. No es probable que funcione de manera confiable con la optimización habilitada (por ejemplo, puede que ni siquiera sea un marco de pila cuando está en línea) y obtener un depurador instalado para que pueda llamar a esa función no será tan simple como usted estaban esperando.

+0

En realidad, muy buen punto en la optimización, estaba asumiendo que esta pregunta estaba relacionada con la instrumentación de depuración .... –

+1

@Ben Voigt: ¿tiene un uso de muestra para 'ICorDebugILFrame :: GetArgument'? –

+0

@Ben Voigt: me gustaría ver un uso de muestra también, si puede proporcionarlo. ¡Gracias por la respuesta! – Pwninstein

6
StackTrace stackTrace = new StackTrace(); 
ParameterInfo[] parameters = stackTrace.GetFrame(1).GetMethod().GetParameters(); 

Nota, GetFrame (1) obtiene el método de llamada en lugar del método actual. Esto debería darle los resultados deseados y le permite ejecutar el siguiente código en LogParameters().

Debería llamar a LogParameters como a continuación, ya que no podría obtener los valores reflejados de integerParameter y stringParameter de ParameterInfo.

LogParameters(integerParameter, stringParameter); 
+0

Definitivamente use un StackTrace para obtener el StackFrame relevante, esto incluso permite declarar la subrutina "logging" con un argumento de "profundidad" opcional. +1 –

+0

La actualización proporciona los valores, pero hay algunas cosas que pueden salir mal. Primero, es una carga de mantenimiento: cualquier cambio en la lista de parámetros debe repetirse en la llamada de registro. En segundo lugar, debe saltar unos pocos aros adicionales si su función original tiene un único parámetro de tipo objeto []. –

3

A menos que utilice el depurador API, no se puede recorrer parámetro valores de un método diferente en la pila de llamadas. Aunque puede obtener el parámetro nombres de la pila de llamadas (como han mencionado otros).

Lo más cercano sería:

public MyFunction(int integerParameter, string stringParameter){ 
    LogParameters(integerParameter, stringParameter); 
} 

public void LogParameters(params object[] values){ 
    // Get the parameter names from callstack and log names/values 
} 
+0

PUEDE, solo necesita la API de depuración, no reflejo –

+0

Este enfoque es una idea clara, pero hay algunas cosas que pueden salir mal. Primero, es una carga de mantenimiento: cualquier cambio en la lista de parámetros debe repetirse en la llamada de registro. En segundo lugar, debe saltar algunos aros adicionales si su función original tiene un único parámetro de tipo 'objeto []'. –

+0

@BenVoigt - sí, es por eso que usaría AOP para hacer tal cosa en estos días. –

1

He seguido las instrucciones y crearon esta clase:

public static class Tracer 
{ 
    public static void Parameters(params object[] parameters) 
    { 
     #if DEBUG 
      var jss = new JavaScriptSerializer(); 

      var stackTrace = new StackTrace(); 

      var paramInfos = stackTrace.GetFrame(1).GetMethod().GetParameters(); 

      var callingMethod = stackTrace.GetFrame(1).GetMethod(); 
      Debug.WriteLine(string.Format("[Func: {0}", callingMethod.DeclaringType.FullName + "." + callingMethod.Name + "]")); 

      for (int i = 0; i < paramInfos.Count(); i++) 
      { 
       var currentParameterInfo = paramInfos[i]; 

       var currentParameter = parameters[i]; 

       Debug.WriteLine(string.Format(" Parameter: {0}", currentParameterInfo.Name)); 

       Debug.WriteLine(string.Format(" Value: {0}", jss.Serialize(currentParameter))); 
      } 
      Debug.WriteLine("[End Func]"); 
     #endif 
    } 
} 

Llámalo así:

public void Send<T>(T command) where T : Command 
{ 
    Tracer.Parameters(command); 
} 

Y la salida es similar a esto

[Func: SimpleCQRS.FakeBus.Send] 
    Parameter: command 
    Value: {"InventoryItemId":"f7005197-bd20-42a6-b35a-15a6dcc23c33","Name":"test record"} 
[End Func] 

Edición

.........

Y extendí mi función trazadora para realmente hacer un gran trabajo para mí. Para rastrear cada función y su función de llamada, etc., puede usar StrackTrace.GetFrame (2) para utilizar funciones adicionales. Y ahora mi producción es mucho más rica. También utilicé la biblioteca Json.NET's para generar objetos JSON con formato agradable. Además, la salida se puede pegar en un archivo JavaScript vacío y ver salida coloreada.

Ahora mi salida tiene el siguiente aspecto:

//Func: HomeController(Constructor): CQRSGui.Controllers.HomeController(Constructor) 
//From: RuntimeTypeHandle.CreateInstance: System.RuntimeTypeHandle.CreateInstance 
var parameters = {} 

//Func: HomeController.Add: CQRSGui.Controllers.HomeController.Add 
//From: System.Object lambda_method(System.Runtime.CompilerServices.Closure, System.Web.Mvc.ControllerBase, System.Object[]) 
var parameters = { 
    "name": "car" 
} 

//Func: Command(Constructor): SimpleCQRS.Command(Constructor) 
//From: CreateInventoryItem(Constructor): SimpleCQRS.CreateInventoryItem(Constructor) 
var parameters = {} 

//Func: CreateInventoryItem(Constructor): SimpleCQRS.CreateInventoryItem(Constructor) 
//From: HomeController.Add: CQRSGui.Controllers.HomeController.Add 
var parameters = { 
    "inventoryItemId": "d974cd27-430d-4b22-ad9d-22ea0e6a2559", 
    "name": "car" 
} 

//Func: FakeBus.Send: SimpleCQRS.FakeBus.Send 
//From: HomeController.Add: CQRSGui.Controllers.HomeController.Add 
var parameters = { 
    "command": { 
     "InventoryItemId": "d974cd27-430d-4b22-ad9d-22ea0e6a2559", 
     "Name": "car" 
    } 
} 

//Func: InventoryCommandHandlers.Handle: SimpleCQRS.InventoryCommandHandlers.Handle 
//From: FakeBus.Send: SimpleCQRS.FakeBus.Send 
var parameters = { 
    "message": { 
     "InventoryItemId": "d974cd27-430d-4b22-ad9d-22ea0e6a2559", 
     "Name": "car" 
    } 
} 

//Func: AggregateRoot(Constructor): SimpleCQRS.AggregateRoot(Constructor) 
//From: InventoryItem(Constructor): SimpleCQRS.InventoryItem(Constructor) 
var parameters = {} 

//Func: InventoryItem(Constructor): SimpleCQRS.InventoryItem(Constructor) 
//From: InventoryCommandHandlers.Handle: SimpleCQRS.InventoryCommandHandlers.Handle 
var parameters = { 
    "id": "d974cd27-430d-4b22-ad9d-22ea0e6a2559", 
    "name": "car" 
} 

//Func: Event(Constructor): SimpleCQRS.Event(Constructor) 
//From: InventoryItemCreated(Constructor): SimpleCQRS.InventoryItemCreated(Constructor) 
var parameters = {} 

//Func: InventoryItemCreated(Constructor): SimpleCQRS.InventoryItemCreated(Constructor) 
//From: InventoryItem(Constructor): SimpleCQRS.InventoryItem(Constructor) 
var parameters = { 
    "id": "d974cd27-430d-4b22-ad9d-22ea0e6a2559", 
    "name": "car" 
} 

//Func: AggregateRoot.ApplyChange: SimpleCQRS.AggregateRoot.ApplyChange 
//From: InventoryItem(Constructor): SimpleCQRS.InventoryItem(Constructor) 
var parameters = { 
    "event": { 
     "Id": "d974cd27-430d-4b22-ad9d-22ea0e6a2559", 
     "Name": "car", 
     "Version": 0 
    } 
} 

Potente, ¿verdad? Solo necesito ver el resultado y no es necesario romper la aplicación una y otra vez y no es necesario verificar en el reloj y las ventanas locales. Me encanta de esta manera. He puesto la función tracker.Parameters en todas partes en mi aplicación y ahora he depurado automáticamente la aplicación.

Una cosa que agregué a mi función de salida fue una llamada a Error Event en la serialización. Y lo manejé desde Json.NET. En realidad, puede caer en un error de referencia circular. Que atrapé Y también, si hay más errores de serialización, puede atraparlos y luego puede mostrar errores de serialización justo debajo de la salida del objeto de parámetros.

+1

Este enfoque es una idea clara, pero hay algunas cosas que pueden salir mal. Primero, es una carga de mantenimiento: cualquier cambio en la lista de parámetros debe repetirse en la llamada de registro.En segundo lugar, debe saltar algunos aros adicionales si su función original tiene un único parámetro de tipo 'objeto []'. –

3

Esta es la clase de utilidad que crea el registro.

internal class ParamaterLogModifiedUtility 
{ 
    private String _methodName; 
    private String _paramaterLog; 

    private readonly JavaScriptSerializer _serializer; 
    private readonly Dictionary<String, Type> _methodParamaters; 
    private readonly List<Tuple<String, Type, object>>_providedParametars; 

    public ParamaterLogModifiedUtility(params Expression<Func<object>>[] providedParameters) 
    { 
     try 
     { 
      _serializer = new JavaScriptSerializer(); 
      var currentMethod = new StackTrace().GetFrame(1).GetMethod(); 

      /*Set class and current method info*/ 
      _methodName = String.Format("Class = {0}, Method = {1}", currentMethod.DeclaringType.FullName, currentMethod.Name); 

      /*Get current methods paramaters*/ 
      _methodParamaters = new Dictionary<string, Type>(); 
      (from aParamater in currentMethod.GetParameters() 
      select new { Name = aParamater.Name, DataType = aParamater.ParameterType }) 
      .ToList() 
      .ForEach(obj => _methodParamaters.Add(obj.Name, obj.DataType)); 

      /*Get provided methods paramaters*/ 
      _providedParametars = new List<Tuple<string, Type, object>>(); 
      foreach (var aExpression in providedParameters) 
      { 
       Expression bodyType = aExpression.Body; 

       if (bodyType is MemberExpression) 
       { 
        AddProvidedParamaterDetail((MemberExpression)aExpression.Body); 
       } 
       else if (bodyType is UnaryExpression) 
       { 
        UnaryExpression unaryExpression = (UnaryExpression)aExpression.Body; 
        AddProvidedParamaterDetail((MemberExpression)unaryExpression.Operand); 
       } 
       else 
       { 
        throw new Exception("Expression type unknown."); 
       } 
      } 

      /*Process log for all method parameters*/ 
      ProcessLog(); 

     } 
     catch (Exception exception) 
     { 
      throw new Exception("Error in paramater log processing.", exception); 
     } 
    } 

    private void ProcessLog() 
    { 
     try 
     { 
      foreach (var aMethodParamater in _methodParamaters) 
      { 
       var aParameter = 
        _providedParametars.Where(
         obj => obj.Item1.Equals(aMethodParamater.Key) && obj.Item2 == aMethodParamater.Value).Single(); 
       _paramaterLog += String.Format(@" ""{0}"":{1},", aParameter.Item1, _serializer.Serialize(aParameter.Item3)); 
      } 
      _paramaterLog = (_paramaterLog != null) ? _paramaterLog.Trim(' ', ',') : string.Empty; 
     } 
     catch (Exception exception) 
     { 
      throw new Exception("MathodParamater is not found in providedParameters."); 
     } 
    } 

    private void AddProvidedParamaterDetail(MemberExpression memberExpression) 
    { 
     ConstantExpression constantExpression = (ConstantExpression) memberExpression.Expression; 
     var name = memberExpression.Member.Name; 
     var value = ((FieldInfo) memberExpression.Member).GetValue(constantExpression.Value); 
     var type = value.GetType(); 
     _providedParametars.Add(new Tuple<string, Type, object>(name, type, value)); 
    } 


    public String GetLog() 
    { 
     return String.Format("{0}({1})", _methodName, _paramaterLog); 
    } 

} 

Uso de la utilidad

class PersonLogic 
{ 
    public bool Add(PersonEntity aPersonEntity, ushort age = 12, String id = "1", String name = "Roy") 
    { 
     string log = new ParamaterLogModifiedUtility(() => aPersonEntity,() => age,() => id,() => name).GetLog(); 
     return true; 
    } 
} 

Ahora Llamando al Usos

class Program 
{ 
    static void Main(string[] args) 
    { 
     try 
     { 
      PersonLogic personLogic = new PersonLogic(); 
      personLogic.Add(id: "1", age: 24, name: "Dipon", aPersonEntity: new PersonEntity() { Id = "1", Name = "Dipon", Age = 24 }); 
     } 
     catch (Exception exception) 
     { 
      Console.WriteLine("Error."); 
     } 
     Console.ReadKey(); 
    } 
} 

registro de resultados:

 Class = MethodParamatersLog.Logic.PersonLogic, Method = Add("aPersonEntity":{"CreatedDateTime":"\/Date(1383422468353)\/","Id":"1","Name":"Dipon","Age":24}, "age":24, "id":"1", "name":"Dipon") 
Cuestiones relacionadas