首页 新闻 会员 周边

C# 方法转为委托类型时时具体做了什么

0
悬赏园豆:10 [已解决问题] 解决于 2022-03-24 14:42

之前发现了一个很有意思的事,如果将方法作为委托参数传递给另一个方,循环N次执行这个操作,好像方法隐式转换成委托时,会有额外的性能消耗,相比直接弄一个匿名委托,要慢很多,代码如下

static void Start() {
            for (int i = 0; i < 5; i++) {
                CompareSpeed(AAA);
                CompareSpeed(BBB);
                CompareSpeed(AAA);
                Console.WriteLine("===========");
            }
        }
        readonly static int len = 1024 * 1024;
        static void CompareSpeed(Action action) {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            action();
            Console.WriteLine(action.Method.Name + ":" + sw.ElapsedTicks);
        }
        static void AAA() {
            for (int i = 0; i < len; i++) {
                Test(DoSomething); 
            }
        }
        static void BBB() {
            for (int i = 0; i < len; i++) {
                Test(() => {
                    int a = 1;
                });
            }
        }
        static void Test(Action action) {
            action();
        }
        static void DoSomething() {
            int a = 1;
        }

结果如下:

AAA:327427
BBB:119771
AAA:302877

AAA:191837
BBB:93651
AAA:200357

AAA:347466
BBB:292075
AAA:234907

AAA:207438
BBB:94038
AAA:189342

AAA:208228
BBB:99733
AAA:187513

在1M的循环次数下,明显可以看到AAA普遍慢于BBB
BBB我理解的是因为里面的匿名函数只创建一次,之后在循环时,都是直接拿已经创建好的
那么AAA慢是不是因为每次都要执行方法隐式转成Action啊,这个隐式转成具体是做了什么啊,我知道委托怎么用,但是不知道委托内部是怎么实现的,网上也收不到相关的内容,有大佬能详细的讲讲吗
而且我发现,如果将循环次数减少,就没有这么明显了,基本都是刚开始的几次AAA比BBB快,次数上来后才体现出BBB比AAA快,这么看的话,是不是AAA的隐式转换要比新创建一个匿名函数要快很多啊,() => { int a = 1; }按我的理解是匿名函数,它和普通的方法有什么区别啊,它可以直接赋值给Action,它的类型是委托吗,还是说它依然是个方法,依然是隐式转换成Action的啊

C#
WmW的主页 WmW | 菜鸟二级 | 园豆:424
提问于:2022-03-22 11:24
< >
分享
最佳答案
0

Test(DoSomething); ==> Test(new Action(DoSomething))

Test(() => {int a = 1;});
==>
static 自动生成的静态类 {static action=new Action(()=>int a=1;)}
Test(自动生成的静态类.action) // 直接引用传递.
//会编译成一个静态类包含这个静态Action方法.

所以AAA方法的多出来的消耗在每次循环的new Action上.

收获园豆:10
czd890 | 专家六级 |园豆:14412 | 2022-03-22 12:23

方法隐式转成Action委托类型,具体是做了什么啊
new Action(DoSomething) 是创建了一个委托实例,然后把DoSomething传进去作为成员了吗,调用action()的时候,内部是怎么处理的啊

WmW | 园豆:424 (菜鸟二级) | 2022-03-23 18:34

@WmW:

Action 就是delegate. Action的定义是 public delegate void Action()
delegate可以大致理解为是一个引用类型 object, 里面保存了传进去的这个方法的函数指针地址.
delegate的定义:

public abstract class Delegate : ICloneable, ISerializable
{
public MethodInfo Method {get;}
public object Target {get;}
}

调用action() 等同于 action.Invoke(); 这行代码也就等于告诉运行时, 我要call 这个函数指针所代表的method.

所以你的测试方法1 每次都要new一个object(delegate, 也就是Action)
方法2 只new一次, 后续的调用都是直接传递这个object引用.

czd890 | 园豆:14412 (专家六级) | 2022-03-24 11:30

至于你的问题, 如果要深究 可以看看文章: https://mattwarren.org/2017/01/25/How-do-.NET-delegates-work/ 以及文章给给出的CLI参考设计文档.

czd890 | 园豆:14412 (专家六级) | 2022-03-24 11:36

@czd890: 明白了,感谢!

WmW | 园豆:424 (菜鸟二级) | 2022-03-24 14:42
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册