购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

建议25:谨慎集合属性的可写操作

如果类型的属性中有集合属性,那么应该保证属性对象是由类型本身产生的。如果将属性设置为可写,则会增加抛出异常的几率。一般情况下,如果集合属性没有值,则它返回的Count等于0,而不是集合属性的值为null。下面的代码将产生一个NullReferenceException异常:


class Program

{

static List<Student>list1=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=list1;

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的修改会直接影响到另一个工作线程中的对象。在例子中,我们将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;


将会导致编译通不过。 EGaD2p8GGEFZciWSPCxSKGOEkoz1mK7Ah7TmvIXT0vLh0ja6zcyL9rCsq6lVXyjA

点击中间区域
呼出菜单
上一章
目录
下一章
×

打开