有没有办法告诉在c#中调用函数的方法的参数?
我正在为我的c#应用程序开发一种免提日志机制 以下是我希望它看起来的样子: 函数有没有办法告诉在c#中调用函数的方法的参数?,c#,.net,reflection,stack-trace,C#,.net,Reflection,Stack Trace,我正在为我的c#应用程序开发一种免提日志机制 以下是我希望它看起来的样子: 函数a(arg1,arg2,arg3…)调用函数b(arg4,arg5,arg6…),后者依次调用log(),后者无法检测堆栈跟踪(这可以通过环境.stacktrace)以及每个函数使用的值(例如a和b)在stacktrace中调用 我希望它在调试和发布模式下工作(或者,至少在调试模式下) 这可以在.net中实现吗?显然不可能: 调用b时,a的arg1使用的堆栈中的空间(IL堆栈,因此可能从未放入堆栈,但在调用时已注册)
a(arg1,arg2,arg3…)
调用函数b(arg4,arg5,arg6…)
,后者依次调用log()
,后者无法检测堆栈跟踪(这可以通过环境.stacktrace
)以及每个函数使用的值(例如a
和b
)在stacktrace中调用
我希望它在调试和发布模式下工作(或者,至少在调试模式下)
这可以在.net中实现吗?显然不可能:
调用b
时,a的arg1
使用的堆栈中的空间(IL堆栈,因此可能从未放入堆栈,但在调用时已注册)不能保证仍被arg1
使用
根据扩展,如果arg1
是引用类型,那么如果调用b
后没有使用它,则不能保证它引用的对象没有被垃圾收集
编辑:
更详细一点,因为你的评论表明你并没有在这方面摸索,并且仍然认为这应该是可能的
抖动使用的调用约定在任何相关标准的规范中都没有指定,这使实现者可以自由地进行改进。32位和64位版本以及不同的版本之间确实存在差异
然而,MS people的文章表明,所使用的公约与公约类似。在您对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
的另一个“调用”的情况下,因为a
的整个调用(包括对b
的调用)可能在一种情况下内联,而在另一种情况下没有内联,并且在另一种情况下内联方式不同。例如,如果代码< > ARG4</代码>直接从另一个调用返回的值,它可以在EAX登记器中,而b
的调用是一个被消除的尾部调用:因为对b
的调用也将由a
立即返回其返回值(或其他一些可能性),然后,a
使用的值将被替换到位,而不是推到堆栈中,并且返回地址发生了变化,因此从b
返回的方法跳回调用a
的方法,跳过了一些工作(并减少了内存使用,使一些会溢出堆栈的函数式方法能够正常工作)。在这种情况下,在调用b
的过程中,a
的参数很可能完全消失,即使是堆栈上的参数
最后一种情况是否应该被视为一种优化,这是一个高度争议的问题;有些语言在很大程度上依赖于它的执行,因为它们提供了良好的性能,而如果它们完全正常工作(而不是溢出堆栈),它们的性能就会非常糟糕
可以有各种各样的其他优化。如果是.NET团队还是Mono团队,应该有各种各样的优化
IEnumerable<T> RepeatedlyInvoke(Func<T> factory, int count)
{
if(count < 0)
throw new ArgumentOutOfRangeException();
while(count-- != 0)
yield return factory();
}