值传递、引用传递和按引用传递

一、值类型和引用类型

值类型:

  包含类型:数值类型、结构体、bool类型、枚举、可空类型(例如:int、double、bool、char、decimal、struct、enum)等等。

  存储特征:数据保存在栈中

 

引用类型:

  包含的类型:数组、委托、接口、object、集合、字符串、用户自定义的类等等。

  存储特征:数据保存在堆中,并在栈里有一个对堆内数据引用

二、传递方式

  按值传递:所有的类型默认是按值传递的。

      值类型传递给方法,会在栈中克隆出一份相同的数据。方法内对数据的更改不会影响方法外的数据

      引用类型传递给方法,会将现有栈中的引用传入方法内,不会增加新数据。方法内对数据的更改会影响方法外的数据,但如果进行了new()操作重新赋值,则会在堆和栈中生成一份新的数据,后续方法内对数据的更新,不会影响方法外的数据

  按引用传递:通过使用ref 、out 关键词,可以对类型按引用传递。对类型实例的所有改动都将影响现有数据

三、通过原理看bug

  1、合理使用按值传递避免bug

  下面这段代码,couponState是枚举,属于值类型,如果在某次循环中对入参couponState做了修改,则后面的循环中即便不没有对couponState做修改,也无法获得刚进入方法时couponState的值了,因为couponState的值已被修改了

 1 private static void UpdateCouponCache(List<GetUserCouponToFcpDto> filterFcp, CouponStateEnum couponState, int operatorTag)
 2         {
 3             foreach (GetUserCouponToFcpDto item in filterFcp)
 4             {
 5                 foreach (CouponChangeDto coupon in item.CouponList)
 6                 {
 7                     UserReceivedCouponEntity couponEntity = GetUserCompanyCouponInfo(item.CustomerId, coupon.OwnerId, coupon.Id);
 8                     if (couponEntity == null || string.IsNullOrEmpty(couponEntity.Id))
 9                     {
10                         continue;
11                     }
12                     if (operatorTag != 0)
13                     {
14                         couponEntity.SurplusCouponAmount += coupon.UesdAmount * operatorTag;
15                     }
16                     if (couponEntity.SurplusCouponAmount < 0)
17                     {
18                         couponEntity.SurplusCouponAmount = 0;
19                     }
20                     if (couponEntity.SurplusCouponAmount > couponEntity.CouponAmount)
21                     {
22                         couponEntity.SurplusCouponAmount = couponEntity.CouponAmount;
23                     }
24                     if (couponState != CouponStateEnum.Freezed)
25                     {
26                         if (couponEntity.SurplusCouponAmount == 0)
27                         {
28                             couponState = CouponStateEnum.Used;
29                         }
30                         if (couponEntity.SurplusCouponAmount == couponEntity.CouponAmount)
31                         {
32                             couponState = CouponStateEnum.NotUsed;
33                         }
34                         if (couponState == CouponStateEnum.PartialUse)
35                         {
36                             if (couponEntity.IsCanUseMultipleTimes == "1" && operatorTag < 0)
37                             {
38                                 couponState = CouponStateEnum.Used;
39                             }
40                         }
41                     }
42                     couponEntity.CouponState = (int)couponState;
43                 }
44             }         
45 }

  解决方法:通过对循环内的方法提取子方法,couponState按值传递,可以保证后续的循环仍可获得方法入参刚进入方法时couponState的值

 1         private static void UpdateCouponCache(List<GetUserCouponToFcpDto> filterFcp, CouponStateEnum couponState, int operatorTag)
 2         {
 3             foreach (GetUserCouponToFcpDto item in filterFcp)
 4             {
 5                 foreach (CouponChangeDto coupon in item.CouponList)
 6                 {
 7                     UserReceivedCouponEntity couponEntity = GetUserCompanyCouponInfo(item.CustomerId, coupon.OwnerId, coupon.Id);
 8                     if (couponEntity == null || string.IsNullOrEmpty(couponEntity.Id))
 9                     {
10                         continue;
11                     }
12                     UpdateCouponInfo(couponState, operatorTag, item, coupon, couponEntity);
13                 }
14             }
15         }
16 
17         private static void UpdateCouponInfo(CouponStateEnum couponState, int operatorTag, GetUserCouponToFcpDto item, CouponChangeDto coupon, UserReceivedCouponEntity couponEntity)
18         {
19             if (operatorTag != 0)
20             {
21                 couponEntity.SurplusCouponAmount += coupon.UesdAmount * operatorTag;
22             }
23             if (couponEntity.SurplusCouponAmount < 0)
24             {
25                 couponEntity.SurplusCouponAmount = 0;
26             }
27             if (couponEntity.SurplusCouponAmount > couponEntity.CouponAmount)
28             {
29                 couponEntity.SurplusCouponAmount = couponEntity.CouponAmount;
30             }
31             if (couponState != CouponStateEnum.Freezed)
32             {
33                 if (couponEntity.SurplusCouponAmount == 0)
34                 {
35                     couponState = CouponStateEnum.Used;
36                 }
37                 if (couponEntity.SurplusCouponAmount == couponEntity.CouponAmount)
38                 {
39                     couponState = CouponStateEnum.NotUsed;
40                 }
41                 if (couponState == CouponStateEnum.PartialUse)
42                 {
43                     if (couponEntity.IsCanUseMultipleTimes == "1" && operatorTag < 0)
44                     {
45                         couponState = CouponStateEnum.Used;
46                     }
47                 }
48             }
49             couponEntity.CouponState = (int)couponState;
50         }

 

  2、合理使用按引用传递避免bug

  下面这段代码中,matchBusLine是引用类型,采用按值传递的方式传递给了方法InitMatchDayBusClassesNotForBack,InitMatchDayBusClassesNotForBack内在特定条件下对matchBusLine进行重新new()的操作,因为采用值传递,new()操作新增了一份数据,不会传递到方法外

 

 

 

   解决方法:给matchBusLine这个参数增加ref关键词,采用按引用传递的方式,从而将方法内的更新传递到方法外

 

posted on 2022-12-17 15:49  sishuiruoshan  阅读(85)  评论(0编辑  收藏  举报

导航