当前位置: 代码迷 >> C# >> C# 语言中 struct 的圈套
  详细解决方案

C# 语言中 struct 的圈套

热度:207   发布时间:2016-05-05 03:08:31.0
C# 语言中 struct 的陷阱

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

01:  namespace Skyiv.Ben02:  {03:    class Employee04:    {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.Ben02:  {03:    class Department04:    {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.Ben05:  {06:    class Program07:    {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.Ben02:  {03:    struct Department04:    {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# 编译器)。

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

三闾大学工资统计表:校长室 人数: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))

版权声明:本文为博主http://www.zuiniusn.com原创文章,未经博主允许不得转载。

2楼u010786678昨天 14:07
谢谢小伙伴的分享,学习了`(*∩_∩*)′
1楼u010786678昨天 14:04
谢谢小伙伴的分享,学习了`(*∩_∩*)′
  相关解决方案