C#语言中struct的陷阱

资讯 驱动中国 sevenleaf 2,442 次阅读 0 条评论
分享 Q

      假设我们要为某大学写一个工资管理程序。首先是表示员工的 Employee 类(Employee.cs):

      01:  namespace Skyiv.Ben

      02:  {

      03:    class Employee

      04:    {

      05:      public string Department { get; private set; }

      06:      public string Name { get; private set; }

      07:      public decimal Salary { get; set; }

      08:

      09:      public Employee(string department, string name, decimal salary)

      10:      {

      11:        Department = department;

      12:        Name = name;

      13:        Salary = salary;

      14:      }

      15:

      16:      public override string ToString()

      17:      {

      18:        return string.Format("{0} {1} 工资:{2:N2}", Department, Name, Salary);

      19:      }

      20:    }

      21:  }

      接着是表示学校中各系的 Department 类(Department.cs):

      01:  namespace Skyiv.Ben

      02:  {

      03:    class Department

      04:    {

      05:      public string Name { get; private set; }

      06:      public int Count { get; private set; }

      07:      public decimal TotalSalary { get; private set; }

      08:

      09:      public Department(string name)

      10:      {

      11:        Name = name;

      12:      }

      13:

      14:      public void Add(Employee employee)

      15:      {

      16:        Count++;

      17:        TotalSalary += employee.Salary;

      18:      }

      19:

      20:      public override string ToString()

      21:      {

      22:        return string.Format("{0} 人数:{1} 总工资:{2:N2}", Name, Count, TotalSalary);

      23:      }

      24:    }

      25:  }

      最后就是主程序 Program.cs :

      01:  using System;

      02:  using System.Collections.Generic;

      03:

      04:  namespace Skyiv.Ben

      05:  {

      06:    class Program

      07:    {

      08:      static void Main()

      09:      {

      10:        new Program().Run();

      11:      }

      12:

      13:      void Run()

      14:      {

      15:        var employees = InitializeEmployees();

      16:        SalaryRaise(employees);

      17:        Statistic(employees);

      18:      }

      19:

      20:      List<Employee> InitializeEmployees()

      21:      {

      22:        var employees = new List<Employee>();

      23:        employees.Add(new Employee("校长室", "高松年", 72767.58m));

      24:        employees.Add(new Employee("政治系", "方鸿渐", 31982.45m));

      25:        employees.Add(new Employee("政治系", "赵辛楣", 40126.31m));

      26:        Console.WriteLine("三闾大学工资明细表:");

      27:        employees.ForEach(e => Console.WriteLine(e));

      28:        return employees;

      29:      }

      30:

      31:      void SalaryRaise(List<Employee> employees)

      32:      {

      33:        for (var i = 0; i < employees.Count; i++) employees[i].Salary += 10000;

      34:        Console.WriteLine(Environment.NewLine + "加薪之后:");

      35:        employees.ForEach(e => Console.WriteLine(e));

      36:      }

      37:

      38:      void Statistic(List<Employee> employees)

      39:      {

      40:        var departments = new Dictionary<string, Department>();

      41:        foreach (var employee in employees)

      42:        {

      43:          var name = employee.Department;

      44:          Department dep;

      45:          if (!departments.TryGetValue(name, out dep)) departments.Add(name, dep = new Department(name));

      46:          dep.Add(employee);

      47:        }

      48:        Console.WriteLine(Environment.NewLine + "三闾大学工资统计表:");

      49:        foreach (var kvp in departments) Console.WriteLine(kvp.Value);

      50:      }

      51:    }

      52:  }

      这个程序的运行结果如下所示:

      三闾大学工资明细表:

      校长室 高松年 工资:72,767.58

      政治系 方鸿渐 工资:31,982.45

      政治系 赵辛楣 工资:40,126.31

      加薪之后:

      校长室 高松年 工资:82,767.58

      政治系 方鸿渐 工资:41,982.45

      政治系 赵辛楣 工资:50,126.31

      三闾大学工资统计表:

      校长室 人数:1 总工资:82,767.58

      政治系 人数:2 总工资:92,108.76

      如果我们把 Employee 类(class)改为结构(struct),则在编译时就会报以下错误:

      CS1612: 无法修改“System.Collections.Generic.List.this[int]”的返回值,因为它不是变量。

      这个错误发生在 Program.cs 第 33 行中的 employees[i].Salary += 10000; 语句。

      我不理解这个 CS1612 错误,如果哪位朋友能够解释一下,请在评论中给出。谢谢!

      如果我们把 Department 类(class)改为结构(struct):

      01:  namespace Skyiv.Ben

      02:  {

      03:    struct Department

      04:    {

      05:      public string Name { get; private set; }

      06:      public int Count { get; private set; }

      07:      public decimal TotalSalary { get; private set; }

      08:

      09:      public Department(string name) : this()

      10:      {

      11:        Name = name;

      12:      }

      13:

      14:      public void Add(Employee employee)

      15:      {

      16:        Count++;

      17:        TotalSalary += employee.Salary;

      18:      }

      19:

      20:      public override string ToString()

      21:      {

      22:        return string.Format("{0} 人数:{1} 总工资:{2:N2}", Name, Count, TotalSalary);

      23:      }

      24:    }

      25:  }

注意上述程序中第 09 行最后要加上“ : this() ”,否则 Microsoft C# 编译器会报错(但是 Mono C# 编译器不会报错,请参见:浅谈 Microsoft C# 编译器和 Mono C# 编译器 <http://www.cnblogs.com/skyivben/archive/2009/05/09/1453007.html>)。

      再次运行该程序,运行结果的最后三行如下所示:

      三闾大学工资统计表:

      校长室 人数:0 总工资:0.00

      政治系 人数:0 总工资:0.00

      这是因为现在的 Department 结构是值类型,而不是引用类型。所以在 Program.cs 第 46 行的 dep.Add(employee); 语句中,dep 的值的更改不会影响到 departments 字典中的值。所以统计出来的人数和总工资都是零了。

      要绕过这个陷阱很简单,在第 46 行的 dep.Add(employee); 语句后面加一句 departments[name] = dep; 就行了。

      在 .NET Framework Base Class Library 中,有很多的结构(struct)。如:

      ·System.Int32

      ·System.DateTime

      ·System.Drawing.Size

      使用时也要小心陷阱。

      此外,还有注意 DateTime 的 Add 和 AddDays 等方法并不更改此 DateTime 的值。而是返回一个新的 DateTime,其值是此运算的结果。因此以下语句是不成立的:

      for (var date = DateTime.MinValue; date < DateTime.Today; date.AddDays(1))

      正确的应该是:

      for (var date = DateTime.MinValue; date < DateTime.Today; date = date.AddDays(1))

版权声明:本文由驱动中国整理发布,转载需注明出处与原文链接。如有疑问请联系 editor@qudong.com
本文价值 成为第一个评分的人
登录后为本文打分
网友评论0 条评论
正在回复 的评论取消
评论加载中…
🔐

登录后参与互动

评论、点赞均可赚积分
连续签到、邀请好友也有奖励 🎁

电脑端: 微信扫码登录 微信内: 一键登录

微信扫一扫

打开微信「扫一扫」,分享本文到朋友圈或好友