Puedo obtener nombres y valores de parámetros en el procedimiento de la función que se está ejecutando actualmente?

Me gustaría hacer algo como esto:

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 ser capaz de automáticamente la salida de nombres y valores de parámetros en tiempo de ejecución a un archivo sin escribiendo explícitamente el código de registro de ellos.

Es posible?

6 Kommentare

  1. 22

    Me doy cuenta de las personas vinculadas a otras preguntas en el que se menciona PostSharp, pero no podía dejar de publicar el código que resolver mi problema (a través de PostSharp) para que otras personas puedan beneficiarse de ella.

    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 agregando el Trace atributo a un método hará muy agradable la depuración de la información a ser de salida, así:

    Debug: Entering MyClass.MyMethod. 
    x = 44
    someString = asdf qwer 1234
    anotherFloat = 3.14
    theBool = True
    Return Value: 45
    Debug: Leaving MyClass.MyMethod.
    • es esto posible con la versión express de postsharp (que es gratuito)?
  2. 12

    Es teóricamente posible con una versión de depuración y optimización de apagado, pero en la práctica, sugiero que usted desea alguna fuente de reescritura de código pasar.

    La gente se va a seguir diciendo reflexión en el trabajo, cuando no, así que esta es la función que realmente capaces de obtener los valores de los argumentos. Es probable que no funcione con las habilitado la optimización (por ejemplo, no podría ni siquiera ser un marco de pila cuando inline es) y llegar a un depurador instalado de modo que usted puede llamar a esa función no será tan simple como que usted estaba esperando.

    • En realidad – muy buen punto en la optimización que se estaba asumiendo esta pregunta estaba relacionada con la depuración de la instrumentación….
    • Voigt – ¿tiene usted un ejemplo de uso para ICorDebugILFrame::GetArgument?
    • Voigt – me gustaría ver un ejemplo de uso así, si usted puede proporcionar. Gracias por la respuesta!
  3. 7
    StackTrace stackTrace = new StackTrace();
    ParameterInfo[] parameters = stackTrace.GetFrame(1).GetMethod().GetParameters();

    Nota, GetFrame(1) se presenta el método de llamada en lugar de que el método actual. Esto te dará los resultados que desea y se le permite ejecutar el código de abajo en LogParameters().

    Sería necesario llamar a LogParameters como el de abajo, ya que usted no será capaz de obtener el reflejo de los valores de integerParameter y stringParameter de ParameterInfo.

    LogParameters(integerParameter, stringParameter);
    • Definitivamente el uso de un StackTrace para obtener el correspondiente entorno de pila, esto incluso permite declarar el «registro» subrutina con un opcional «profundidad» argumento. +1
    • La actualización proporciona los valores, pero hay un par de cosas que pueden salir mal. En primer lugar, es una carga de mantenimiento — los cambios a la lista de parámetros se tienen que repetir en el registro de la llamada. En segundo lugar, usted tiene que saltar a través de unos aros de si su función original tiene un único parámetro de tipo object[].
  4. 3

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

    La cosa más cercana 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
    }
    • usted PUEDE, usted necesidad justa de la API de depuración, no de la reflexión
    • Este enfoque es una buena idea, pero hay un par de cosas que pueden salir mal. En primer lugar, es una carga de mantenimiento — los cambios a la lista de parámetros se tienen que repetir en el registro de la llamada. En segundo lugar, usted tiene que saltar a través de unos aros de si su función original tiene un único parámetro de tipo object[].
    • sí, por eso me gustaría uso de AOP para hacer tal cosa en estos días.
  5. 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);
        }
    
    }

    Utilizando la Utilidad de

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

    Resultado De Registro:

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

    He seguido las instrucciones y crea 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
        }
    }

    Llamada de esta manera:

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

    Y se ve el resultado de este

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

    Edición

    ………

    Y extendí mi trazador función para hacer un gran trabajo para mí. Para trazar cada función y su función de llamada, etc., usted puede utilizar StrackTrace.GetFrame(2) utilizar la funcionalidad añadida. Y ahora mi salida es mucho más rica. También he usado Json.NET’s de la biblioteca a la salida de aspecto agradable con formato JSON objetos. También la salida puede ser pegado en vacío de un archivo JavaScript y ver coloreada de salida.

    Ahora mi salida se parece a esto:

    //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, ¿no? Solo me falta ver la salida y no es necesario romper la aplicación de nuevo y de nuevo y no es necesario registrarse en el reloj y locales de windows. Me encanta esta manera. He puesto el tracker.Parameters función en todas partes en mi solicitud y me han automáticamente depurar la aplicación.

    Una cosa que he añadido a mi salida de la función fue una llamada a Evento de Error en la serialización. Y manejado de Json.NET. En realidad, usted puede caer en una referencia circular de error. El que me llamó. Y también si hay más de serialización de errores se pueden capturar y, a continuación, puede mostrar la serialización de los errores justo debajo de los parámetros de salida del objeto.

    • Este enfoque es una buena idea, pero hay un par de cosas que pueden salir mal. En primer lugar, es una carga de mantenimiento — los cambios a la lista de parámetros se tienen que repetir en el registro de la llamada. En segundo lugar, usted tiene que saltar a través de unos aros de si su función original tiene un único parámetro de tipo object[].

Kommentieren Sie den Artikel

Bitte geben Sie Ihren Kommentar ein!
Bitte geben Sie hier Ihren Namen ein

Pruebas en línea