gpt4 book ai didi

c# - 有什么方法可以告诉在C#中调用函数的方法的参数吗?

转载 作者:行者123 更新时间:2023-12-03 19:38:18 25 4
gpt4 key购买 nike

我正在为我的C#应用​​程序开发一种自动日志机制。

这是我想要的样子:

function a(arg1, arg2, arg 3.....)调用函数b(arg4,arg5,arg6....),该函数依次调用log(),该Environment.StackTrace能够检测stacktrace(可以通过a完成)以及调用stacktrace中每个函数的值(例如b和ojit_code)。

我希望它在调试和 Release模式下工作(或至少在 Debug模式下工作)。

可以在.net中执行此操作吗?

最佳答案

可能是不可能的:

b调用时,a的arg1使用的堆栈中的空间(IL堆栈,因此可能甚至从未放入堆栈中,但已在调用中注册),不能保证arg1仍在使用该空间。

通过扩展,如果arg1是引用类型,则不能保证所引用的对象没有被垃圾回收(如果在调用b之后未使用该对象)。

编辑:

稍微多一点,因为您的评论表明您并没有在意这点,但仍然认为应该可行。

规范中没有为任何相关标准指定抖动使用的调用约定,这使实现者可以自由地进行改进。实际上,它们在32位和64位版本以及不同的发行版之间确实有所不同。

但是,来自MS人士的文章建议使用的约定类似于__fastcall约定。在您对a的调用中,将arg1放入ECX寄存器*,并将arg2放入EDX寄存器(我假设32位x86进行了简化,其中注册了amd64,甚至更多的参数)正在运行代码的核心。 arg3将被压入堆栈,并且确实存在于内存中。

请注意,此时,arg1arg2不存在内存位置,它们仅在CPU寄存器中。

在执行方法本身的过程中,必要时使用寄存器和存储器。然后b被调用。

现在,如果a将需要arg1arg2,则必须在调用b之前将其推送。但是,如果没有,那么就不会-甚至可能会重新排序以减少这种需求。相反,到目前为止,这些寄存器可能已经被用于其他用途-抖动并不愚蠢,因此,如果它需要一个寄存器或堆栈上的一个插槽,而其余的方法都没有使用,那就是重用该空间。 (为此,在此之上的级别,C#编译器将重用IL生成的虚拟堆栈中的插槽)。

因此,当调用b时,arg4放置在寄存器ECX中,arg5放入EDX中,arg6压入堆栈。在这一点上,arg1arg2不存在,您再也无法找出它们的含义,因此,在将书回收并变成卫生纸后,您可以再读一本书。

(有趣的是,一种方法在相同位置调用另一个具有相同参数的方法是很普遍的,在这种情况下,可以不理会ECX和EDX)。

然后,b返回,将其返回值放在EAX寄存器或EDX:EAX对中,或在EAX指向它的内存中,根据大小,a在将返回值放入该寄存器之前还要做更多的工作,依此类推。

现在,这假设尚未进行任何优化。实际上,有可能根本没有调用b,而是内联了它的代码。在这种情况下,值是在寄存器中还是在堆栈中-在后者的情况下,它们在堆栈中的位置都不再与b的签名有关,而与a期间的相关值无关的执行,并且在另一个“调用”到b的情况下,甚至在另一个“调用”从ba的情况下,都会有所不同,因为整个a调用(包括其对b的调用)都可以被内联在一种情况下,没有内联,而在另一种情况下又内联。例如,如果arg4直接来自另一个调用返回的值,则此刻可能在EAX寄存器中,而arg5在ECX中,因为它与arg1相同,而arg6在中间的中间a使用的堆栈空间。

另一种可能性是,对b的调用是一个尾部调用,因此被消除了:因为对b的调用也将立即由a(或其他可能性)立即返回其返回值,所以,而不是将其压入堆栈,替换a使用的值,并更改返回地址,以便从b返回的内容跳回到称为a的方法,从而跳过了一些工作(并在某种程度上减少了内存使用,因为某些功能样式接近会使堆栈溢出,而确实可以很好地工作)。在这种情况下,在对b的调用期间,对a的参数可能已经完全消失,即使那些已经在堆栈中的参数也是如此。

最后一种情况是否甚至应该被认为是最优化是值得商highly的。一些语言在很大程度上依赖于它的完成,因为它们提供了良好的性能,即使它们完全可以工作,也不会给它们带来可怕的性能(而不是使堆栈溢出)。

可以有其他各种优化方式。 应该是的所有其他优化方式-如果.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呢?即使在第一次调用时,它也是不同的,并且并非上面的代码是奇怪的代码。

因此,总而言之:
  • 抖动:通常会注册参数。您可以期望x86将2个指针,引用或整数参数放入寄存器中,而amd64将4个指针,引用或整数参数以及4个浮点参数放入寄存器中。他们没有位置可以读取它们。
  • 抖动:堆栈上的参数通常会被覆盖。
  • 抖动:可能根本没有真正的调用,因此没有地方可以查找参数,因为它们可能在任何地方。
  • 抖动:“调用”可能正在重新使用与上一个相同的帧。
  • 编译器:IL可能会为本地人重新使用插槽。
  • 人力:程序员可以更改参数值。

  • 综上所述,到底怎么知道 arg1是什么呢?

    现在,添加垃圾回收的存在。试想一下,尽管如此,我们是否能神奇地知道什么是 arg1。如果这是对堆上对象的引用,那么它可能仍然对我们没有好处,因为如果以上所有内容都意味着堆栈上没有更多的引用处于 Activity 状态,那么很明显这确实会发生-并且GC启动,那么该对象可能已被收集。因此,我们可以神奇地掌握的是对不再存在的内容的引用-实际上很可能是对堆中现在用于其他用途的区域的支持,bang整个框架的整个类型安全!

    它与反射获得IL丝毫没有可比性,因为:
  • IL是静态的,而不只是给定时间点的状态。同样,与我们第一次阅读它们时得到的反馈相比,我们可以更轻松地从图书馆中获得我们喜欢的书的副本。
  • 无论如何,IL不会反射(reflect)内联等的影响。如果每次实际使用时都内联一个调用,然后我们使用反射来获取该方法的MethodBody,则通常内联的调用是无关紧要的。

  • 关于性能分析,AOP和拦截的其他答案中的建议与您将要得到的非常接近。

    *实际上, this是实例成员的真正第一个参数。让我们假装一切都是静态的,所以我们不必一直指出这一点。

    关于c# - 有什么方法可以告诉在C#中调用函数的方法的参数吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12116429/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com