Can I get parameter names/values procedurally from the currently executing function?

后端 未结 6 1359
猫巷女王i
猫巷女王i 2020-12-02 14:38

I would like to do something like this:

public MyFunction(int integerParameter, string stringParameter){
    //Do this:
    LogParameters();
    //Instead of         


        
相关标签:
6条回答
  • 2020-12-02 14:44
    StackTrace stackTrace = new StackTrace();
    ParameterInfo[] parameters = stackTrace.GetFrame(1).GetMethod().GetParameters();
    

    Note, GetFrame(1) gets the calling method rather than the current method. This should give you your desired results and allow you to execute the code below in LogParameters().

    You would need to call LogParameters like below since you wouldn't be able to get the reflected values of integerParameter and stringParameter from ParameterInfo.

    LogParameters(integerParameter, stringParameter);
    
    0 讨论(0)
  • 2020-12-02 14:48

    It's theoretically possible with a debug build and optimization turned off, but practically speaking, I suggest you want some source code rewriting pass.

    People are going to keep telling you reflection will work when it won't, so here is the function that's actually capable of getting argument values. It's not likely to work reliably with optimization enabled (for example, there might not even be a stack frame when inlining is on) and getting a debugger installed so you can call that function won't be nearly as simple as you were hoping.

    0 讨论(0)
  • 2020-12-02 14:49

    Unless you use the debugger API, you cannot loop through parameter values of a different method on the call stack. Though you can get the parameter names from the callstack (as others have mentioned).

    The closest thing would be:

    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 讨论(0)
  • 2020-12-02 14:55

    This is the Utility class that creates the log.

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

    Using the Utility

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

    Now Calling the Usages

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

    Result Log:

            Class = MethodParamatersLog.Logic.PersonLogic, Method = Add("aPersonEntity":{"CreatedDateTime":"\/Date(1383422468353)\/","Id":"1","Name":"Dipon","Age":24}, "age":24, "id":"1", "name":"Dipon")
    
    0 讨论(0)
  • 2020-12-02 14:57

    I realize people linked to other questions which mentioned PostSharp, but I couldn't help posting the code that solved my problem (using PostSharp) so other people could benefit from it.

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

    Simply adding the Trace attribute to a method will cause very nice debugging information to be output, like so:

    Debug: Entering MyClass.MyMethod. 
    x = 44
    someString = asdf qwer 1234
    anotherFloat = 3.14
    theBool = True
    Return Value: 45
    Debug: Leaving MyClass.MyMethod.
    
    0 讨论(0)
  • 2020-12-02 15:04

    I followed the instructions and created this 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
        }
    }
    

    Call it like this:

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

    And the output looks like this

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

    Editing

    .........

    And I extended my tracer function to really do a great job for me. To trace every function and its calling function, etc., you can use StrackTrace.GetFrame(2) to use added functionality. And now my output is much richer. I also used Json.NET's library to output nice looking formatted JSON objects. Also the output can be pasted in an empty JavaScript file and see colorized output.

    Now my output looks like this:

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

    Powerful, isn't it? I just need to see the output and don't need to break the application again and again and don't need to check into watch and local windows. I love this way. I have put the tracker.Parameters function in everywhere in my application and I have now automatically debugged the application.

    One thing which I added to my outputting function was a call to Error Event in serialization. And handled that from Json.NET. Actually you can fall into a circular reference error. Which I caught. And also if there are more serialization errors you can catch them and then you can display serialization errors just below the parameters object output.

    0 讨论(0)
提交回复
热议问题