C# 我可以从当前执行的函数中按程序获取参数名/值吗?
我想这样做:C# 我可以从当前执行的函数中按程序获取参数名/值吗?,c#,.net,reflection,logging,parameters,C#,.net,Reflection,Logging,Parameters,我想这样做: public MyFunction(int integerParameter, string stringParameter){ //Do this: LogParameters(); //Instead of this: //Log.Debug("integerParameter: " + integerParameter + // ", stringParameter: " + stringParameter); }
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.
}
public void Send<T>(T command) where T : Command
{
Tracer.Parameters(command);
}
//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
}
}
我甚至不确定我想做什么是可能的,但如果能够在运行时自动将参数名称/值输出到文件中,而不显式地编写代码来记录它们,那将是非常好的
有可能吗?理论上,关闭调试构建和优化是可能的,但实际上,我建议您需要一些源代码重写过程 人们会不断地告诉你,当它不起作用时,反思会起作用,所以。在启用优化的情况下,它不可能可靠地工作(例如,当内联处于启用状态时,甚至可能没有堆栈帧),安装调试器以便调用该函数也不会像您希望的那样简单
StackTrace stackTrace = new StackTrace();
ParameterInfo[] parameters = stackTrace.GetFrame(1).GetMethod().GetParameters();
注意,GetFrame(1)获取调用方法,而不是当前方法。这将为您提供所需的结果,并允许您在LogParameters()中执行下面的代码
由于无法从ParameterInfo获取integerParameter和stringParameter的反映值,因此需要像下面这样调用LogParameters
LogParameters(integerParameter, stringParameter);
除非使用调试器,否则无法循环调用堆栈上其他方法的参数值。尽管您可以从callstack获取参数名(正如其他人所提到的) 最接近的是:
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
}
我意识到人们会链接到其他提到PostSharp的问题,但我忍不住发布了解决我问题的代码(使用PostSharp),以便其他人可以从中受益
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);
}
}
我按照说明创建了这个类:
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
}
}
编辑
我扩展了我的跟踪功能,为我做了一件很棒的工作。要跟踪每个函数及其调用函数等,可以使用StrackTrace.GetFrame(2)来使用添加的功能。现在我的产出更丰富了。我还使用这个库输出漂亮的格式化JSON对象。还可以将输出粘贴到空JavaScript文件中,并查看彩色输出
现在,我的输出如下所示:
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.
}
public void Send<T>(T command) where T : Command
{
Tracer.Parameters(command);
}
//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
}
}
很强大,不是吗?我只需要查看输出,不需要一次又一次地破坏应用程序,也不需要检查手表和本地窗口。我喜欢这样。我已经将tracker.Parameters
函数放在我的应用程序的任何地方,现在我已经自动调试了应用程序
我在输出函数中添加的一件事是调用序列化中的错误事件。并从Json.NET处理了这个问题。实际上,您可能会陷入循环引用错误。我抓住了。此外,如果存在更多序列化错误,您可以捕获它们,然后可以在参数对象输出下方显示序列化错误。这是创建日志的实用程序类
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);
}
}
现在叫用法
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();
}
}
结果日志:
Class = MethodParamatersLog.Logic.PersonLogic, Method = Add("aPersonEntity":{"CreatedDateTime":"\/Date(1383422468353)\/","Id":"1","Name":"Dipon","Age":24}, "age":24, "id":"1", "name":"Dipon")
接近于…的副本,并且这个副本明确地使用StackTrace来获取相关的StackFrame,这甚至允许使用可选的“depth”参数声明“logging”子例程+1更新提供了值,但也有一些地方可能出错。首先,这是一个维护负担——对参数列表的任何更改都必须在日志调用中重复。其次,如果您的原始函数只有一个object[]类型的参数,那么您必须跳过一些额外的环。您可以,您只需要调试API,而不是Reflection。这种方法是一个好主意,但是有一些事情可能会出错。首先,这是一个维护负担——对参数列表的任何更改都必须在日志调用中重复。其次,如果原始函数只有一个类型为
object[]
@BenVoigt-yes的参数,则必须跳过一些额外的环,这就是为什么这些天我会使用AOP来做这样的事情。事实上,在优化过程中,这一点非常好。我假设这个问题与调试工具有关…@Ben Voigt-您是否有ICorDebugILFrame::GetArgument
的示例用法?@Ben Voigt-如果您可以提供,我也想看看示例用法。谢谢你的回答!postsharp的快速版(免费)有可能做到这一点吗?这种方法是一个好主意,但也有一些地方可能出错。首先,这是一个维护负担——对参数列表的任何更改都必须在日志调用中重复。其次,如果原始函数只有一个类型为object[]
的参数,则必须跳过几个额外的环。