【转】编写高质量代码改善C#程序的157个建议——建议25:谨慎集合属性的可写操作
建议25:谨慎集合属性的可写操作
如果类型的属性中有集合属性,那么应该保证属性对象是由类型本身产生的。如果将属性设置为可写,则会增加抛出异常的几率。一般情况下,如果集合属性没有值,则它返回的Count等于0,而不是集合属性的值为null。下面的代码将产生一个NullReferenceException异常:
class Program { static List<Student> listStudent = new List<Student>() { new Student(){ Name = "Mike", Age = 1}, new Student(){ Name = "Rose", Age = 2} }; static void Main(string[] args) { StudentTeamA teamA = new StudentTeamA(); Thread t1 = new Thread(() => { teamA.Students = listStudent; Thread.Sleep(3000); Console.WriteLine(listStudent.Count); //模拟对 //集合属性进行一些运算 }); t1.Start(); Thread t2 = new Thread(() => { listStudent = null; //模拟在别的地方对list1而 //不是属性本身赋值为null }); t2.Start(); } } class Student { public string Name { get; set; } public int Age { get; set; } } class StudentTeamA { public List<Student> Students { get; set; } }
上面代码的问题是:线程t1模拟将对类型StudentTeamA的Students属性进行赋值,它是一个可读/可写的属性。由于集合属性是一个引用类型,而当前针对该属性对象的引用却又两个,即集合本身和调用者的类型变量listStudent,线程t2也许是另一个程序员写的,但他看到的只有listStudent,结果,针对listStudent的修改hi直接影响到另一个工作线程的对象。在例子中,我们将listStudent赋值为null,模拟在StudentTeamA(或者说工作线程t1)不知情的情况下使得集合属性变为null。接着,线程t1模拟针对Students属性进行若干操作,导致抛出异常。
下面的StudentTeamA版本是一个改进过的版本。首先,将类型的集合属性设置为只读;其次,集合对象由类型自身创建,这保证了集合属性永远只有一个引用:
class Program { static List<Student> listStudent = new List<Student>() { new Student(){ Name = "Mike", Age = 1}, new Student(){ Name = "Rose", Age = 2} }; static void Main(string[] args) { StudentTeamA teamA2 = new StudentTeamA(); teamA2.Students.Add(new Student() { Name = "Steve", Age = 3 }); teamA2.Students.AddRange(listStudent); Console.WriteLine(teamA2.Students.Count); //也可以像下面这样实现 StudentTeamA teamA3 = new StudentTeamA(listStudent); Console.WriteLine(teamA3.Students.Count); } class Student { public string Name { get; set; } public int Age { get; set; } } class StudentTeamA { public List<Student> Students { get; private set; } public StudentTeamA() { Students = new List<Student>(); } public StudentTeamA(IEnumerable<Student> studentList) : this() { Students.AddRange(studentList); } }
在改进版本的StudentTeamA中尝试对属性Students进行赋值:
teamA.Students = listStudent;
将导致编译通不过。
转自:《编写高质量代码改善C#程序的157个建议》陆敏技
鹰击长空,鱼翔浅底