我正在为我的c#应用程序的解除日志机制工作。有没有什么办法可以告诉在c#中调用函数的方法的参数?
这里是想什么我它看起来像:
功能a(arg1, arg2, arg 3.....)
调用函数b(arg4,arg5,arg6....)
,进而调用log()
比能够检测堆栈跟踪(这可以通过Environment.StackTrace
完成),并与价值观调用堆栈跟踪中的每个函数(例如,a
和b
)。
我希望它能够在调试和发布模式下工作(或者至少在调试模式下)。
这是可以做.net?
我正在为我的c#应用程序的解除日志机制工作。有没有什么办法可以告诉在c#中调用函数的方法的参数?
这里是想什么我它看起来像:
功能a(arg1, arg2, arg 3.....)
调用函数b(arg4,arg5,arg6....)
,进而调用log()
比能够检测堆栈跟踪(这可以通过Environment.StackTrace
完成),并与价值观调用堆栈跟踪中的每个函数(例如,a
和b
)。
我希望它能够在调试和发布模式下工作(或者至少在调试模式下)。
这是可以做.net?
可证明不可能的:
的时候b
被调用时,所使用的堆栈空间的arg1
(的IL栈,所以可能有人甚至从来没有把在堆栈中,但已经enregistered上呼叫)不保证仍然使用arg1
。
通过扩展,如果arg1
是引用类型,那么它所引用的对象不保证没有被垃圾回收,如果它在b
的调用之后没有被使用。
编辑:
一些详细信息,因为您的意见建议你不所著的Grokking这一点,仍然认为这是可能的。
抖动使用的调用约定没有在任何相关标准的规范中指定,这使实施者可以自由地进行改进。它们确实在32位和64位版本以及不同版本之间有所不同。
但是,来自MS人员的文章建议使用的约定类似于__fastcall约定。在您拨打a
时,arg1
将被放入ECX寄存器*和arg2
,放入运行代码的核心的EDX寄存器(我简化为假设32位x86,其中amd64甚至更多参数已注册) 。 arg3
将被推入堆栈并确实存在于内存中。
请注意,此时,没有内存位置,其中arg1
和arg2
存在,它们只在CPU寄存器中。
在执行方法本身的过程中,必要时使用寄存器和存储器。并调用b
。
现在,如果a
将需要arg1
或arg2
它将不得不在推出之前它呼吁b
。但是,如果没有,那么它不会 - 甚至可能会重新排序以减少这种需求。相反,这些寄存器可能已经用于其他方面 - 抖动不是愚蠢的,所以如果它需要一个寄存器或栈上的一个槽,并且有一个未被用于剩余的方法,它就会重用这个空间。 (就此而言,在此之上的级别上,C#编译器将重新使用IL生成的虚拟堆栈中的插槽)。
因此,当调用b
时,arg4
被放入寄存器ECX中,arg5
放入EDX中并且arg6
被推入栈中。在这一点上,arg1
和arg2
不存在,你不能再发现他们是什么,而不是你可以阅读一本书后,它已被回收利用,变成卫生纸。
(有趣的注意的是,它是很常见的一种方法与在相同位置相同的参数,在这种情况下,ECX和EDX可以只单独留在家中调用另一个)。
然后,b
返回,将其返回值放入EAX寄存器或EDX:EAX对中或带有EAX的内存中,指向它的大小取决于大小,a
在将其返回寄存器之前做了一些更多工作,等等。现在
,这是假设有没有做过任何的优化。实际上,有可能根本没有调用b
,而是将其代码内联。在这种情况下,无论在寄存器中还是在堆栈上 - 以及在后一种情况下它们在堆栈中的哪个值,都不再与b
的签名有关,并且与a
期间的相关值的相关处并且在另一个“呼叫”到b
的情况下,或者甚至在从a
的另一个“呼叫”到b
的情况下,这将是不同的,因为包括呼叫b
的呼叫在内的整个呼叫a
可能已经内联在另一种情况下,不在另一种情况下插入,而在另一种情况下插入不同。举例来说,如果,arg4
从另一个调用返回的值开门见山,也可能是在这一点上,EAX寄存器,而arg5
在ECX,因为它是一样arg1
和arg6
在的中间的某个中途堆栈空间正在使用a
。
另一种可能性是调用b
是被淘汰尾部调用:因为调用b
将不得不立即a
回到过它的返回值(或一些其他的可能性),然后,而不是推到,正在使用的a
值堆栈被替换就地,和返回地址改变,以使得从b
返回跳转回调用a
,跳过一些工作(和减少存储器的使用的程度的方法,该方法的一些功能风格的方法,会溢出堆栈,而不是工作,确实很好)。在这种情况下,在致电b
期间,a
的参数可能会完全消失,即使是那些已经在堆栈中的参数。
这是非常值得商榷是否最后这种情况下应,即使在所有被认为是优化;一些语言在很大程度上取决于它是如何完成的,它们提供了良好的性能,并且如果它们甚至可以工作(而不是溢出堆栈),它们也不会带来可怕的性能。
可以有其他所有优化方式。有应该是所有其他优化的方式 - 如果.NET团队或Mono团队做的东西,使我的代码更快或使用更少的内存,但其他行为相同,没有我必须的东西,我一个人不会抱怨!
,这就是假设编写C#在首位的人从未改变一个参数,它肯定不会是真正的价值。考虑以下代码:
IEnumerable<T> RepeatedlyInvoke(Func<T> factory, int count)
{
if(count < 0)
throw new ArgumentOutOfRangeException();
while(count-- != 0)
yield return factory();
}
即使C#编译器和抖动都在,你可以保证参数在上述的方式没有改变这种浪费的方式设计,你怎么会知道什么count
就已经来自factory
的调用?即使在第一次调用时,它也是不同的,并且不是上面的代码是奇怪的。
因此,简言之:
从这一切,如何才有可能知道什么是arg1
是什么?
现在,添加垃圾收集的存在。想象一下,如果我们能够神奇地知道什么arg1
是无论如何,尽管所有这一切。如果它是对堆中某个对象的引用,那么它可能对我们没有好处,因为如果上述所有内容都意味着堆栈中没有更多的引用被激活 - 并且应该清楚的是,这确实发生了 - 并且GC启动,那么该对象可能已被收集。所以我们可以魔法般地获得的东西是对已经不存在的东西的引用 - 事实上很可能是堆中的某个区域现在被用于其他东西,而是让整个框架的整个类型安全!
这不是在可比反射获得IL一点半点,因为:
MethodBody
,那么它通常内联的事实是不相关的。有关性能分析,AOP和拦截的其他答案中的建议与您将要得到的一样接近。
*实际上,this
是实例成员真正的第一个参数。让我们假装一切都是静态的,所以我们不必一直指出这一点。
感谢您的好评 – tito11
在.net中是不可能的。在运行时,JITter可能决定使用CPU寄存器而不是堆栈来存储方法参数,甚至可以重写堆栈中的初始(传递)值。因此.net允许在源代码中的任何一点记录参数对于性能来说是非常高性价比的。
据我所知,唯一的办法就是使用.net CLR分析API。 (例如Typemock框架能够做这样的事情,它使用CLR剖析API)
如果您只需要拦截虚函数/属性(包括接口方法/属性)调用,则可以使用任何拦截框架(Unity或Castle例如)。
大约有.NET分析API的一些信息:
这不是在C#中有可能,你应该使用AOP方法和执行方法的参数记录何时调用每个方法。这样你可以集中你的日志代码,使它可以重用,然后你只需要标记哪些方法需要参数日志记录。
我相信这可以很容易地实现使用AOP框架,如PostSharp。
可能不会发生没有类型嘲讽或ICorDebug的魔法。 即使StackFrame类也只列出允许您获取有关源的信息的成员,而不是参数。
然而,您所使用的功能作为IntelliTrace与方法记录一起存在。您可以过滤需要审核的内容。
有趣的问题...不可能在C#中,但也许在IL或使用反射? –
我敢打赌,这是不可能的。尽管这可能会产生一个非常规的非常规面试问题(想象一下这可能 - 你认为它会起作用吗?),比大多数典型的“古怪问题”要好得多;揭示C#和CLR的整体知识的一个很好的起点。 –
从理论上讲,这应该是可能的。序数堆栈跟踪返回正在调用的函数的信息,以及它们的参数类型,用于调用函数的所有变量都应该存储在堆栈的某处(尽管可能是局部变量)。 –