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

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

    class Program
{
static List<Student> listStudent = new List<Student>()
{
new Student(){ Name = "Mike", Age = },
new Student(){ Name = "Rose", Age = }
}; static void Main(string[] args)
{
StudentTeamA teamA = new StudentTeamA();
Thread t1 = new Thread(() =>
{
teamA.Students = listStudent;
Thread.Sleep();
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 = },
new Student(){ Name = "Rose", Age = }
}; static void Main(string[] args)
{
StudentTeamA teamA2 = new StudentTeamA();
teamA2.Students.Add(new Student() { Name = "Steve", Age = });
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个建议》陆敏技

编写高质量代码改善C#程序的157个建议——建议25:谨慎集合属性的可写操作的更多相关文章

  1. 编写高质量代码改善C#程序的157个建议[1-3]

    原文:编写高质量代码改善C#程序的157个建议[1-3] 前言 本文主要来学习记录前三个建议. 建议1.正确操作字符串 建议2.使用默认转型方法 建议3.区别对待强制转换与as和is 其中有很多需要理 ...

  2. 读书--编写高质量代码 改善C#程序的157个建议

    最近读了陆敏技写的一本书<<编写高质量代码  改善C#程序的157个建议>>书写的很好.我还看了他的博客http://www.cnblogs.com/luminji . 前面部 ...

  3. 编写高质量代码改善C#程序的157个建议——建议157:从写第一个界面开始,就进行自动化测试

    建议157:从写第一个界面开始,就进行自动化测试 如果说单元测试是白盒测试,那么自动化测试就是黑盒测试.黑盒测试要求捕捉界面上的控件句柄,并对其进行编码,以达到模拟人工操作的目的.具体的自动化测试请学 ...

  4. 编写高质量代码改善C#程序的157个建议——建议156:利用特性为应用程序提供多个版本

    建议156:利用特性为应用程序提供多个版本 基于如下理由,需要为应用程序提供多个版本: 应用程序有体验版和完整功能版. 应用程序在迭代过程中需要屏蔽一些不成熟的功能. 假设我们的应用程序共有两类功能: ...

  5. 编写高质量代码改善C#程序的157个建议——建议155:随生产代码一起提交单元测试代码

    建议155:随生产代码一起提交单元测试代码 首先提出一个问题:我们害怕修改代码吗?是否曾经无数次面对乱糟糟的代码,下决心进行重构,然后在一个月后的某个周一,却收到来自测试版的报告:新的版本,没有之前的 ...

  6. 编写高质量代码改善C#程序的157个建议——建议154:不要过度设计,在敏捷中体会重构的乐趣

    建议154:不要过度设计,在敏捷中体会重构的乐趣 有时候,我们不得不随时更改软件的设计: 如果项目是针对某个大型机构的,不同级别的软件使用者,会提出不同的需求,或者随着关键岗位人员的更替,需求也会随个 ...

  7. 编写高质量代码改善C#程序的157个建议——建议153:若抛出异常,则必须要注释

    建议153:若抛出异常,则必须要注释 有一种必须加注释的场景,即使异常.如果API抛出异常,则必须给出注释.调用者必须通过注释才能知道如何处理那些专有的异常.通常,即便良好的命名也不可能告诉我们方法会 ...

  8. 编写高质量代码改善C#程序的157个建议——建议152:最少,甚至是不要注释

    建议152:最少,甚至是不要注释 以往,我们在代码中不写上几行注释,就会被认为是钟不负责任的态度.现在,这种观点正在改变.试想,如果我们所有的命名全部采用有意义的单词或词组,注释还有多少存在的价值. ...

  9. 编写高质量代码改善C#程序的157个建议——建议151:使用事件访问器替换公开的事件成员变量

    建议151:使用事件访问器替换公开的事件成员变量 事件访问器包含两部分内容:添加访问器和删除访问器.如果涉及公开的事件字段,应该始终使用事件访问器.代码如下所示: class SampleClass ...

  10. 编写高质量代码改善C#程序的157个建议——建议150:使用匿名方法、Lambda表达式代替方法

    建议150:使用匿名方法.Lambda表达式代替方法 方法体如果过小(如小于3行),专门为此定义一个方法就会显得过于繁琐.比如: static void SampeMethod() { List< ...

随机推荐

  1. delphi 2010安装unidac

    UniDAC是一个功能强大的非可视化跨数据库的数据访问组件,可用于Delphi,Delphi for .NET,C++Builder,and Lazarus (Free Pascal).它提供了对流行 ...

  2. python‘s tenth day for me

    动态参数    *args   **kwargs   *args  动态参数,万能参数 # args 接受的就是实参对应的  所有位置参数,并将其放在元祖中. def func(*args): pri ...

  3. Flask之单元测试

    5.2单元测试 为什么要测试? Web程序开发过程一般包括以下几个阶段:[需求分析,设计阶段,实现阶段,测试阶段].其中测试阶段通过人工或自动来运行测试某个系统的功能.目的是检验其是否满足需求,并得出 ...

  4. 摄影之HDR

    摄影之HDR 高动态范围图像(High-Dynamic Range,简称HDR),相比普通的图像,可以提供更多的动态范围和图像细节,根据不同的曝光时间的LDR(Low-Dynamic Range)图像 ...

  5. 解析Java反射 - invoke方法

    最近工作中涉及到获取同程火车票,大概描述为:将本地获取的发出城市,目的城市及出发时间按固定格式封装,调用接口获取可乘坐座席等级最高的火车票,接口返回数据用包含三层类封装的类接受,接受的类总共为四层,倒 ...

  6. zookpeer的安装与配置

    zookpeer集群搭建: 集群搭建过程简介: 这里准3台服务器做zk(zookpeer下面简称zk)集群搭建: zk集群由一个leader和两个follower组成,对外端口默认为2181端口,关于 ...

  7. 【原】Coursera—Andrew Ng机器学习—课程笔记 Lecture 6_Logistic Regression 逻辑回归

    Lecture6 Logistic Regression 逻辑回归 6.1 分类问题 Classification6.2 假设表示 Hypothesis Representation6.3 决策边界 ...

  8. ZooKeeper集群搭建过程

    ZooKeeper集群搭建过程 提纲 1.ZooKeeper简介 2.ZooKeeper的下载和安装 3.部署3个节点的ZK伪分布式集群 3.1.解压ZooKeeper安装包 3.2.为每个节点建立d ...

  9. Aws s3 api

    PUT操作的这个实现将一个对象添加到一个bucket中. 您必须具有对bucket的WRITE权限才能向其中添加对象. Amazon S3从不添加部分对象; 如果您收到成功响应,则Amazon S3将 ...

  10. pyspider用PyQuery解析页面数据

    示例说明: 本示例主要是PyQuery解析返回的response页面数据.response.doc解析页面数据是pyspider的主要用法,应该熟练掌握基本使用方法.其他返回类型示例见后续文章. py ...