42 votes

Puis-je obtenir des noms / valeurs de paramètres de manière procédurale à partir de la fonction en cours d'exécution?

J'aimerais faire quelque chose comme ça:

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

Je ne sais même pas ce que je veux faire est possible, mais il serait très agréable de pouvoir exporter automatiquement les noms / valeurs de paramètres au moment de l'exécution dans un fichier sans écrire explicitement le code permettant de les consigner.

C'est possible?

22voto

Pwninstein Points 7293

Je sais que des personnes sont liées à d'autres questions qui mentionnent PostSharp, mais je ne pouvais pas m'empêcher de poster le code qui résolvait mon problème (en utilisant PostSharp) afin que d'autres personnes puissent en bénéficier.

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

Ajouter simplement l'attribut Trace à une méthode provoquera la sortie de très bonnes informations de débogage, comme ceci:

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

12voto

Ben Voigt Points 151460

Il est théoriquement possible avec une version de débogage et d'optimisation éteint, mais dans la pratique, je suggère vous voulez de la source de réécriture de code pass.

Les gens vont garder racontant réflexion de travail quand il n'est pas, donc, ici, est la fonction qui est en fait capable d'obtenir des valeurs d'argument. Il n'est pas susceptible de fonctionner de manière fiable avec l'optimisation activée (par exemple, il pourrait même ne pas être un cadre de pile lors de l'inlining est sur) et l'obtention d'un débogueur installés de sorte que vous pouvez appeler cette fonction ne sera pas aussi simple que vous espériez.

8voto

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

Remarque: 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 devez appeler LogParameters comme ci-dessous, car vous ne pourriez pas obtenir les valeurs reflétées de integerParameter et stringParameter à partir de ParameterInfo.

 LogParameters(integerParameter, stringParameter);
 

3voto

Peter Lillevold Points 20689

Sauf si vous utilisez l' API du débogueur, vous ne pouvez pas parcourir les valeurs de paramètre d'une autre méthode sur la pile d'appels. Bien que vous puissiez obtenir les noms de paramètres à partir de la pile d'appels (comme d'autres l'ont mentionné).

La chose la plus proche serait:

 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
}
 

3voto

Dipon Roy Points 86

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

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

}
 

Utiliser l'utilitaire

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

Appelons maintenant les utilisations

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

Journal des résultats:

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X