params、out、ref修饰符在委托中的适配差异及原因咨询
//Main method test6 _300er = new(yetan.aa); _300er.Invoke(45, 78); test7 _200lr = new(yetan.bb); _200lr.Invoke(new int[4]); test8 max8 = new(yetan.dd); double rty = 45; max8.Invoke(ref rty); test9 max9 = new(yetan.cc); max9.Invoke(78); test10 neo1 = new(yetan.ee); neo1.Invoke(out char ytss); test11 neo2 = new(yetan.ff); neo2.Invoke('^'); delegate void test6(params int[] h); delegate void test7(int[] h); delegate void test8(ref double j); delegate void test9(double j); delegate void test10(out char j); delegate void test11(char j); class yetan { public static void aa(int[] y) { Console.WriteLine("params dude"); } public static void bb(params int[] y) { Console.WriteLine("params dude"); } public static void cc(double y) { Console.WriteLine("jhjgbrvf"); } public static void dd(ref double y) { Console.WriteLine("jhjgbrvf"); } public static void ee(out char y) { y = '%'; Console.WriteLine("vvvvvvv"); } public static void ff(char y) { Console.WriteLine("vvvvvv"); } }
我声明了带有params、out、ref修饰符以及无修饰符的委托,并创建了与委托签名对应的方法。经测试发现:带params修饰符的委托test6可指向无params修饰符的方法aa,调用时方法aa会表现得如同带有params修饰符;反之无params的委托test7也可指向带params的方法bb,最终由委托决定是否启用params特性。但out和ref修饰符不具备这种灵活性:若委托test8未声明ref修饰符,则无法指向带ref的方法dd。请问这种差异背后的技术原因是什么?
差异背后的技术原因
一、params是语法糖,不改变方法核心签名
params本质只是C#编译器提供的语法便利,它不会修改方法在CLR层面的核心签名:
- 带
params的方法编译后,生成的IL代码和普通数组参数方法完全一致,params仅允许调用者写代码时传入多个离散参数,由编译器自动打包成数组传递给方法。 - 因此不管委托有没有
params,只要参数类型是对应数组,就能和目标方法匹配:- 带
params的委托指向无params方法时,传入的离散参数会被编译器自动打包成数组,而目标方法本来就接受数组参数,自然能正常执行。 - 无
params的委托指向带params方法时,直接传递数组即可——方法本身的核心签名就是接受数组,params只是调用时的语法糖,不影响委托与方法的匹配逻辑。
- 带
二、ref/out是CLR层面的参数传递约定,属于签名核心部分
ref和out是CLR定义的参数传递规则,直接影响方法的元数据签名:
- 带
ref/out的参数,在CLR中传递的是变量的内存地址(而非值的副本),这种传递方式会被编码到方法的元数据里,成为方法签名不可分割的一部分。 - 委托的签名必须与目标方法严格一致:如果方法用了
ref/out,委托必须声明对应修饰符,否则CLR会认为这是两个完全不同的签名——不带ref/out的委托调用时传递值副本,而带ref/out的方法需要接收内存地址,两者参数传递逻辑完全不兼容,编译器和CLR都不允许这种不匹配的绑定。
内容的提问来源于stack exchange,提问作者Eric Movsessian
相关产品推荐
相关产品推荐

