Posso ottenere i nomi / i valori dei parametri proceduralmente dalla funzione attualmente in esecuzione?

Mi piacerebbe fare qualcosa di simile a questo:

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

Non sono nemmeno sicuro di cosa sia ansible fare, ma sarebbe molto bello poter generare automaticamente nomi / valori dei parametri in fase di esecuzione su un file senza scrivere esplicitamente il codice per registrarli.

È ansible?

Realizzo persone legate ad altre domande che hanno menzionato PostSharp, ma non ho potuto fare a meno di postare il codice che risolveva il mio problema (usando PostSharp) in modo che altre persone potessero trarne beneficio.

 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, verranno generate informazioni di debug molto belle, come ad esempio:

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

È teoricamente ansible con una build di debug e l’ottimizzazione distriggersta, ma in pratica, suggerisco di volere un passaggio di riscrittura del codice sorgente.

Le persone continueranno a dirti che la riflessione funzionerà quando non lo farà, quindi ecco la funzione che è effettivamente in grado di ottenere valori argomento . Non è probabile che funzioni in modo affidabile con l’ottimizzazione abilitata (ad esempio, potrebbe non esserci nemmeno uno stack frame quando onlining è attivo) e ottenere un debugger installato in modo da poter chiamare quella funzione non sarà così semplice come speravate.

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

Nota: GetFrame (1) ottiene il metodo di chiamata piuttosto che 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 sarebbe ansible ottenere i valori riflessi di integerParameter e stringParameter da ParameterInfo.

 LogParameters(integerParameter, stringParameter); 

A meno che non si usi l’ API del debugger, non è ansible eseguire il loop dei valori dei parametri di un metodo diverso nello stack di chiamate. Sebbene tu possa ottenere i nomi dei parametri dal callstack (come altri hanno menzionato).

La cosa più vicina sarebbe:

 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 } 

Questa è la class di utilità che crea il registro.

 internal class ParamaterLogModifiedUtility { private String _methodName; private String _paramaterLog; private readonly JavaScriptSerializer _serializer; private readonly Dictionary _methodParamaters; private readonly List>_providedParametars; public ParamaterLogModifiedUtility(params Expression>[] 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(); (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>(); 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(name, type, value)); } public String GetLog() { return String.Format("{0}({1})", _methodName, _paramaterLog); } } 

Utilizzo dell’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 chiama gli 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") 

Ho seguito le istruzioni e ho creato questa class:

 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 così:

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

E l'output è come questo

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

La modifica

.........

E ho esteso la mia funzione di tracciante per fare davvero un ottimo lavoro per me. Per tracciare tutte le funzioni e le relative funzioni di chiamata, ecc., È ansible utilizzare StrackTrace.GetFrame (2) per utilizzare funzionalità aggiunte. E ora la mia produzione è molto più ricca. Ho anche usato la libreria di Json.NET 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 il mio risultato è simile a questo:

 //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 windows locali. Adoro in questo modo. Ho inserito 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 nella 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, è ansible catturarli e visualizzare gli errori di serializzazione appena sotto l'output dell'object Parametri.