《Effective C#》读书笔记——条目26:避免返回对内部类对象的引用<使用C#表达设计>

简介:

  可能你会认为只读属性就只能读取,调用者不可能改变属性值。并非所有的情况都是如此,我们看下面的示例:

复制代码
 1     public class MyBusinessObject
 2     {
 3         private List<string> listOfData = new List<string> {"COM1","COM2","COM3","COM4","COM5","COM6" };
 4 
 5         public List<string> Data
 6         {
 7             get { return listOfData; }
 8         }
 9         //其他细节省略
10     }
复制代码

本来我们希望MyBusinessOjbect类的Data属性是只读的,但是现在仍然可以通过其返还的引用来改变其内部状态:

1             MyBusinessObject bo = new MyBusinessObject();
2             //访问集合
3             List<string> stuff = bo.Data;
4             //不应该这些,但是这里却允许
5             stuff.Clear();

  显然,这并不是我们希望的行为,我们为类创建了严格的接口,然后让用户通过接口使用对象。我们不希望用户在我们不知道的情况下,访问或者修改对象的内部状态。我们有四种策略可以防止这种类型的内部数据结构遭受有意或无意的修改

  • 值类型
  • 常量类型
  • 接口
  • 包装器(Wrapper)

 

1.值类型

  当客户代码通过属性来访问值类型的成员时,实际返回的是值类型的副本,而对副本的任何修改都不会影响对象的内部状态。

 

2.常量类型

   常量类型也是安全的(参见:Effective C# 笔记条目20)。我们可以在类型中安全地返回字符串或者其他的常量类型,客户代码不可能对它们做出任何更改,因为我们也可以保证类型内部状态的安全。

 

3.定义接口

   通过定义接口,将客户对内部数据成员的访问权限限制在一部分功能中(参见:Effective C# 笔记条目22)。当我们创建类时,可以创建一组接口来支持类功能的子集。通过接口向外界暴露类的功能,即可尽量地避免内部数据遭到有意或无意的更改。使用IEnumerable接口向外提供List<T>的功能就是这种策略的一个应用。

 

4.提供包装器对象

  提供一个包装器对象,仅暴露该包装器,从而限制对其中对象的访问。System.Collections.ObjectModel.ReadOnlyCollection<T>类型就是对集合的一个标准的只读封装,让其中的数据保持只读:

复制代码
 1     public class MyBusinessObject
 2     {
 3         private List<string> listOfData = new List<string> {"COM1","COM2","COM3","COM4","COM5","COM6" };
 4 
 5         public List<string> Data
 6         {
 7             get { return listOfData; }
 8         }
 9 
10         public ReadOnlyCollection<string> CollectionOfData
11         {
12             get { return new ReadOnlyCollection<string>(listOfData); }
13         }
14         //其他细节省略
15     }
复制代码

 

小节

如果将引用类型通过公有借口暴露给外界,那么对象的使用者即可绕过我们定义的方法和属性来更改对象的内部结构。这违反了我们通常的直觉,也会导致常见的错误。考虑到这一点,你应该修改类暴露出的接口。如果只是简单的返回内部数据,那么实际上就给外界赋予了访问内部成员的权限。客户代码可以调用成员中任何可用的方法。通过使用接口、包装器对象或值类型向外部提供私有数据,即可限制外界对这些数据的访问能力。

本文转自gyzhao博客园博客,原文链接:http://www.cnblogs.com/IPrograming/archive/2013/01/19/EffectiveCSharp_26.html ,如需转载请自行联系原作者
相关文章
|
3月前
|
Java C#
C# 面向对象编程解析:优势、类和对象、类成员详解
OOP代表面向对象编程。 过程式编程涉及编写执行数据操作的过程或方法,而面向对象编程涉及创建包含数据和方法的对象。 面向对象编程相对于过程式编程具有几个优势: OOP执行速度更快,更容易执行 OOP为程序提供了清晰的结构 OOP有助于保持C#代码DRY("不要重复自己"),并使代码更易于维护、修改和调试 OOP使得能够创建完全可重用的应用程序,编写更少的代码并减少开发时间 提示:"不要重复自己"(DRY)原则是有关减少代码重复的原则。应该提取出应用程序中常见的代码,并将其放置在单一位置并重复使用,而不是重复编写。
51 0
|
1月前
|
存储 C#
C#对象和类
C#对象和类
15 0
|
3月前
|
存储 C#
C#基础语法(类和对象)
C#基础语法(类和对象)
20 2
|
4月前
|
Java 编译器 C#
【从Java转C#】第三章:对象和类型
【从Java转C#】第三章:对象和类型
|
9月前
|
C#
C#——类和对象
C#——类和对象
54 0
|
4月前
|
XML 存储 JSON
C# | 使用Json序列化对象时忽略只读的属性
将对象序列化成为Json字符串是一个使用频率非常高的功能。Json格式具有很高的可读性,同时相较于XML更节省空间。 在开发过程中经常会遇到需要保存配置的场景,比如将配置信息保存在配置类型的实例中,再将这个对象序列化成为Json字符串并保存。当需要加载配置时,则是读取Json格式的字符串再将其还原成配置对象。在序列化的过程中,默认会将所有公开的属性和字段都序列化进入Json字符串中,这其中也会包含只读的属性或字段,而只读的属性和字段在反序列化的过程中其实是无意义的,也就是说这一部分存储是多余的。 本文将讲解如何在执行Json序列化时,忽略掉那些只读的属性和字段。
53 0
C# | 使用Json序列化对象时忽略只读的属性
|
8月前
|
C#
《More Effective C# 》读书笔记 第一章
《More Effective C# 》读书笔记 第一章
|
9月前
|
开发框架 .NET 编译器
C# Lambda表达式和linq表达式 之 匿名对象查询接收
C# Lambda表达式和linq表达式 之 匿名对象查询接收
|
前端开发 程序员 C#
【C#】通过扩展对象的方式,对字符串等数据类型进行数据进一步处理
在本篇文章中,我们讲一起了解下对象扩展的使用 在实际项目开发中,对象扩展使用的场景还是挺多的,比如:需要对时间值进行再处理,或者字符串中的斜杠(/)转为反斜杠(\)
90 0
|
安全 Java 程序员
C#编程学习17:类与对象学习小结
C#编程学习17:类与对象学习小结
C#编程学习17:类与对象学习小结