翼度科技»论坛 编程开发 .net 查看内容

关于C#方法中引用类型参数传递

4

主题

4

帖子

12

积分

新手上路

Rank: 1

积分
12
本文叙述的问题的根源在于对C#基础知识掌握不牢固,从而在遇到难以理解的问题时浪费了大量时间;
在此也警示自己,该啃啃基础书籍了!
话不多说,先上代码:
先看问题
  1. services.AddEasyCaching(option =>
  2. {
  3.     option.UseHybrid(config =>
  4.     {
  5.         .....
  6.     })
  7.     .WithZookeeeperBus(busConf =>
  8.     {
  9.         // 方式1
  10.         busConf.ConnectionString = "192.168.3.86:2181";
  11.         // 方式2
  12.         busConf = new ZkBusOptions
  13.         {
  14.             ConnectionString = "192.168.3.86:2181",
  15.         };
  16.     });
  17. });
复制代码
这是我在使用EasyCaching时的一段注册代码,在配置Zookeeeper Bus时调用了WithZookeeeperBus(Action)拓展方法,并采用了如上两种赋值方式,看似都能正常配置,但实际不尽人意:
方式1:配置正常;
方式2:配置无效,最终ConnectionString=null;
what?肯定是编译器问题!( 确信
此时我的心情极其复杂,这两种赋值不是一样的?

问题在哪?

一开始,我甚至怀疑是自己对Action的理解不到位,存在某种机制导致的问题,但事实证明,饭可以乱吃,锅不能乱甩;
每每想起这,不由得老脸一红!
问题在这

实则问题是在最基础的方法传参问题;
咱们可以先理解一下方法传参存在哪几种情况,以及情况对应传参方式;
在C#参数传递分为如下四种:


  • 值类型的值传递(将值类型的副本传递给方法)
  • 值类型的引用传递(将值类型本身传递给方法,例如:使用了ref int a)
  • 引用类型的值传递(将引用的副本传递给方法 )
  • 引用类型的引用传递(将引用本身传递给方法,例如:使用了ref object o)
此时,如果你已经悟了,那文章到此结束,还一知半解的,请继续往下看;
再细一点

先看看如下代码:
  1. void Main()
  2. {
  3.         var p = new Person { Name = "jason", Age = 19 };
  4.     // 赋值方法 1
  5.         AssignmentPerson_1(p);
  6.     Console.WriteLine($"Name: {p.Name}; Age: {p.Age}");
  7.     // 赋值方法 2
  8.     AssignmentPerson_2(p);
  9.         Console.WriteLine($"Name: {p.Name}; Age: {p.Age}");
  10. }
  11. public void AssignmentPerson_1(Person t)
  12. {
  13.         var np = new Person { Name = "jack", Age = 18 };
  14.         t = np;
  15. }
  16. public void AssignmentPerson_2(Person t)
  17. {
  18.         var np zhanzhi
复制代码
很显然,在本文起始的教训下,答案是明显,会输出如下:
Name = "jason"; Age = 19
Name = "jack"; Age = 18
首先,在main中创建了p,假设p在栈中的地址为0x0001,栈值为指向堆中实例的地址0x00D1;

赋值方法-1

然后,开始调用AssignmentPerson_1(Person t),并传入p;
此时,不是直接将 p(栈地址:0x0001)传入方法,而是拷贝了一份p(栈地址:0x0005),并且同时将栈值赋为0x00D1,传入方法中;
所以,AssignmentPerson_1(Person t)中t的地址为0x0005;

最后,新建np,栈地址为0x000A,栈值为指向堆中实例的地址0x00D5;
并将t = np,t的栈值被替换了0x00D1 -> 0x00D5;

最终,p的栈值还是0x00D1,并且0x00D1中的堆值也并未发生变化,所以赋值无效;
赋值方法-2

同理,当调用AssignmentPerson_2(Person t)时,同样传入p,同样新建np;
但是,并没有替换t的栈值,它仍旧与p指向的堆地址相同,为0x00D1;
此时赋值操作只是替换了堆值中实例的属性值:t.Name = np.Name,t.Age = np.Age;

所以,t指向的堆值发生了变化, t与p又指向地址相同,p的实例属性值也就发生了变化;
总结

该读一本《CLR via》了!
参考

1-关于C#函数对象参数传递的问题
2-彻底澄清:C#方法参数

来源:https://www.cnblogs.com/memoyu/archive/2023/02/17/17131079.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具