2010-03-08 13 views
39

Vorrei fare qualcosa di simile:È possibile ottenere i nomi/i valori dei parametri proceduralmente dalla funzione attualmente in esecuzione?

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. 
} 

Io non sono nemmeno sicuro di quello che voglio fare è possibile, ma sarebbe molto bello per essere in grado di emettere automaticamente i nomi dei parametri/valori in fase di esecuzione a un file senza scrivere esplicitamente il codice per registrarli.

È possibile?

+2

Close-a-duplicato di http://stackoverflow.com/domande/135782/generic-logging-di-funzione-parametri-a-eccezioni-Handl ing –

+0

... e questo http://stackoverflow.com/questions/819576/is-it-possibile-per-aggiungere-parameters-values-for-each-frame-in-call-stack-in-net –

+0

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

risposta

21

Mi rendo conto che le persone legate ad altre domande che di cui PostSharp, ma non ho potuto fare a postare il codice che ha risolto il mio problema (usando PostSharp) in modo altre persone potrebbero beneficiare da.

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); 
    } 
} 

semplicemente aggiungendo l'attributo Trace a un metodo causerà molto bello informazioni di debug da emettere, in questo modo:

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

è possibile con la versione esplicita di postsharp (che è gratuita)? – Dbl

12

È teoricamente possibile con una compilazione di debug e l'ottimizzazione disattivata, ma in pratica, suggerisco di volere un passaggio di riscrittura del codice sorgente.

Le persone continueranno a dirti che il riflesso funzionerà quando non lo sarà, quindi here is the function that's actually capable of getting argument values. Non è probabile che funzioni in modo affidabile con l'ottimizzazione abilitata (ad esempio, potrebbe non essere nemmeno essere uno stack frame quando è attivo l'inlining) e ottenere un debugger installato in modo da poter chiamare quella funzione non sarà così semplice come speravamo.

+0

In realtà - ottimo punto sull'ottimizzazione - stavo assumendo che questa domanda fosse correlata alla strumentazione di debug .... –

+1

@Ben Voigt - avete un utilizzo di esempio per 'ICorDebugILFrame :: GetArgument'? –

+0

@Ben Voigt - Mi piacerebbe vedere anche un esempio di utilizzo, se è possibile fornirlo. Grazie per la risposta! – Pwninstein

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

Nota, GetFrame (1) ottiene il metodo di chiamata anziché il metodo corrente. Questo dovrebbe darti i risultati desiderati e permettere di eseguire il codice qui sotto in LogParameters().

È necessario chiamare LogParameters come di seguito poiché non è possibile ottenere i valori riflessi di integerParameter e stringParameter da ParameterInfo.

LogParameters(integerParameter, stringParameter); 
+0

Utilizzare definitivamente uno StackTrace per ottenere lo StackFrame rilevante, ciò consente persino di dichiarare la subroutine "logging" con un argomento opzionale "depth". +1 –

+0

L'aggiornamento fornisce i valori, ma ci sono alcune cose che possono andare storte. Innanzitutto, è un onere di manutenzione: qualsiasi modifica all'elenco dei parametri deve essere ripetuta nella chiamata di registrazione. In secondo luogo, devi passare attraverso alcuni cerchi aggiuntivi se la tua funzione originale ha un singolo parametro di tipo oggetto []. –

3

A meno che non si utilizza il debugger API, non è possibile collegare attraverso il parametro valori di un metodo diverso sul stack di chiamate. Anche se è possibile ottenere il parametro nomi dal callstack (come altri hanno menzionato).

La cosa più vicina potrebbe essere:

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

PUOI, hai solo bisogno dell'API di debug, non di riflessione –

+0

Questo approccio è un'idea chiara, ma ci sono alcune cose che possono andare storte. Innanzitutto, è un onere di manutenzione: qualsiasi modifica all'elenco dei parametri deve essere ripetuta nella chiamata di registrazione. In secondo luogo, devi passare attraverso alcuni cerchi extra se la tua funzione originale ha un singolo parametro di tipo 'oggetto []'. –

+0

@ BenVoigt - sì, è per questo che userei AOP per fare una cosa del genere in questi giorni. –

1

ho seguito le istruzioni e ha creato questa classe:

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 
    } 
} 

Chiamalo in questo modo:

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

E l'uscita assomiglia a questo

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

Editing

.........

E ho esteso la mia funzione di tracciamento per fare davvero un ottimo lavoro per me. Per tracciare tutte le funzioni e le relative funzioni di chiamata, ecc., È possibile utilizzare StrackTrace.GetFrame (2) per utilizzare funzionalità aggiunte. E ora la mia produzione è molto più ricca. Ho anche usato la libreria Json.NET's per produrre oggetti JSON formattati dall'aspetto gradevole. Inoltre, l'output può essere incollato in un file JavaScript vuoto e visualizzare l'output colorato.

Ora la mia uscita si presenta così:

//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, non è vero? Ho solo bisogno di vedere l'output e non c'è bisogno di rompere l'applicazione ancora e ancora e non ho bisogno di controllare in orologio e finestre locali. Adoro in questo modo. Ho messo la funzione tracker.Parameters ovunque nella mia applicazione e ora ho eseguito il debug automatico dell'applicazione.

Una cosa che ho aggiunto alla mia funzione di output era una chiamata all'Errore Event in serializzazione. E gestito quello da Json.NET. In realtà puoi cadere in un errore di riferimento circolare. Che ho catturato. Inoltre, se sono presenti più errori di serializzazione, è possibile catturarli e visualizzare gli errori di serializzazione appena sotto l'output dell'oggetto Parametri.

+1

Questo approccio è un'idea chiara, ma ci sono alcune cose che possono andare storte. Innanzitutto, è un onere di manutenzione: qualsiasi modifica all'elenco dei parametri deve essere ripetuta nella chiamata di registrazione.In secondo luogo, devi passare attraverso alcuni cerchi extra se la tua funzione originale ha un singolo parametro di tipo 'oggetto []'. –

3

Questa è la classe di utilità che crea il 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); 
    } 

} 

mediante l'utilità

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; 
    } 
} 

Ora Chiamando l'Usi

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 dei risultati:

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