Puis-je obtenir les noms / valeurs de parameters de la fonction en cours d’exécution?

Je voudrais faire quelque chose comme ça:

public MyFunction(int integerParameter, ssortingng ssortingngParameter){ //Do this: LogParameters(); //Instead of this: //Log.Debug("integerParameter: " + integerParameter + // ", ssortingngParameter: " + ssortingngParameter); } 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. } 

Je ne suis même pas sûr de ce que je veux faire, mais ce serait bien de pouvoir générer automatiquement les noms / valeurs de parameters à l’exécution dans un fichier sans écrire explicitement le code pour les enregistrer.

C’est possible?

Je me rends compte des personnes liées à d’autres questions qui ont mentionné PostSharp, mais je n’ai pas pu m’empêcher de publier le code qui a résolu mon problème (en utilisant PostSharp) pour que d’autres personnes puissent en bénéficier.

 class Program { static void Main(ssortingng[] 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, ssortingng someSsortingng, float anotherFloat, bool theBool) { return x + 1; } } [Serializable] public sealed class TraceAtsortingbute : OnMethodBoundaryAspect { private readonly ssortingng category; public TraceAtsortingbute(ssortingng category) { this.category = category; } public ssortingng Category { get { return category; } } public override void OnEntry(MethodExecutionArgs args) { Trace.WriteLine(ssortingng.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); } } 

Le simple ajout de l’atsortingbut Trace à une méthode entraînera la sortie de très bonnes informations de débogage, comme ceci:

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

C’est théoriquement possible avec une version de débogage et une optimisation désactivée, mais en pratique, je suggère que vous souhaitiez une certaine réécriture du code source.

Les gens vont continuer à vous dire que la reflection fonctionnera quand ce ne sera pas le cas, alors voici la fonction qui est réellement capable d’obtenir des valeurs d’argument . Cela ne fonctionnera probablement pas correctement avec l’optimisation activée (par exemple, il pourrait ne pas y avoir de frame de stack lorsque l’inline est activé) et l’installation d’un débogueur pour appeler cette fonction ne sera pas aussi simple que vous l’espériez.

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

Notez que GetFrame (1) obtient la méthode d’appel plutôt que la méthode actuelle. Cela devrait vous donner les résultats souhaités et vous permettre d’exécuter le code ci-dessous dans LogParameters ().

Vous devrez appeler LogParameters comme ci-dessous car vous ne pourrez pas obtenir les valeurs reflétées de integerParameter et ssortingngParameter à partir de ParameterInfo.

 LogParameters(integerParameter, ssortingngParameter); 

À moins d’utiliser l’ API du débogueur, vous ne pouvez pas parcourir les valeurs de paramètre d’une autre méthode sur la stack d’appels. Bien que vous puissiez obtenir les noms de parameters à partir de la stack d’appels (comme d’autres l’ont mentionné).

La chose la plus proche serait:

 public MyFunction(int integerParameter, ssortingng ssortingngParameter){ LogParameters(integerParameter, ssortingngParameter); } public void LogParameters(params object[] values){ // Get the parameter names from callstack and log names/values } 

C’est la classe Utility qui crée le journal.

 internal class ParamaterLogModifiedUtility { private Ssortingng _methodName; private Ssortingng _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 = Ssortingng.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 += Ssortingng.Format(@" ""{0}"":{1},", aParameter.Item1, _serializer.Serialize(aParameter.Item3)); } _paramaterLog = (_paramaterLog != null) ? _paramaterLog.Trim(' ', ',') : ssortingng.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 Ssortingng GetLog() { return Ssortingng.Format("{0}({1})", _methodName, _paramaterLog); } } 

Utilisation de l’utilitaire

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

Appelant maintenant les utilisations

 class Program { static void Main(ssortingng[] 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(); } } 

Journal de résultat:

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

J’ai suivi les instructions et créé cette 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(ssortingng.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 } } 

Appelez-le comme ceci:

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

Et la sortie ressemble à ceci

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

Édition

.........

Et j'ai élargi ma fonction de suivi pour vraiment faire du bon travail pour moi. Pour tracer chaque fonction et sa fonction appelante, etc., vous pouvez utiliser StrackTrace.GetFrame (2) pour utiliser des fonctionnalités supplémentaires. Et maintenant, mon résultat est beaucoup plus riche. J'ai également utilisé la bibliothèque de Json.NET pour générer de jolis objects JSON formatés. La sortie peut également être collée dans un fichier JavaScript vide et voir la sortie colorisée.

Maintenant, ma sortie ressemble à ceci:

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

Puissant, n'est-ce pas? J'ai juste besoin de voir le résultat et je n'ai pas besoin de casser l'application encore et encore et je n'ai pas besoin de vérifier dans les fenêtres de surveillance et locales. J'aime cette façon. J'ai mis la fonction tracker.Parameters partout dans mon application et j'ai maintenant débogué automatiquement l'application.

Une chose que j'ai ajoutée à ma fonction de sortie était un appel à l'événement d'erreur dans la sérialisation. Et géré cela depuis Json.NET. En fait, vous pouvez tomber dans une erreur de référence circulaire. Ce que j'ai attrapé Et si vous rencontrez davantage d'erreurs de sérialisation, vous pouvez les détecter et afficher les erreurs de sérialisation juste en dessous de la sortie de l'object Paramètres.