首页 / 软件开发 / C# / Effective C#原则23:避免返回内部类对象的引用
Effective C#原则23:避免返回内部类对象的引用2010-12-11 博客园 Wu.Country@侠缘译你已经知道,所谓的只读属性就是指调用者无法修改这个属性。不幸运的是 ,这并不是一直有效的。如果你创建了一个属性,它返回一个引用类型,那么调 用者就可以访问这个对象的公共成员,也包括修改这些属性的状态。例如:public class MyBusinessObject
{
// Read Only property providing access to a
// private data member:
private DataSet _ds;
public DataSet Data
{
get
{
return _ds;
}
}
}
// Access the dataset:
DataSet ds = bizObj.Data;
// Not intended, but allowed:
ds.Tables.Clear( ); // Deletes all data tables.
任何MyBusinessObject的公共客户都可以修改你的内部 dateset。你创建的属性用来隐藏类的内部数据结构,你提供了方法,让知道该 方法的客户熟练的操作数据。因此,你的类可以管理内部状态的任何改变。然而 ,只读属性对于类的封装来说开了一个后门。当你考虑这些问题时,它并不是一 个可读可写属性,而是一个只读属性。欢迎来到一个精彩的基于引用的 系统,任何返回引用的成员都会返回一个对象的句柄。你给了调用者一个接口的 句柄,因此调用者修改这个对象的某个内部引用时,不再需要通过这个对象。很清楚,你想防止这样的事情发生。你为你的类创建了一个接口,同时 希望用户使用这个接口。你不希望用户在不明白你的意图时,访问并修改对象的 内部状态。你有四个策略来保护你的内部数据结构不被无意的修改:值类型,恒 定类型,接口和包装(模式)。值类型在通过属性访问时,是数据的拷贝 。客户对类的拷贝数据所做的任何修改,不会影响到对象的内部状态。客户可以 根据需求随意的修改拷贝的数据。这对你的内部状态没有任意影响。恒 定类型,例如System.String,也是安全的。你可以返回一个字符串,或者其它 恒定类型。恒定类型的安全性告诉你,没有客户可以修改字符串。你的内部状态 是安全的。第三个选择就是定义接口,从而充许客户访问内部成员的部 份功能(参见原则19)。当你创建一个自己的类时,你可以创建一些设置接口,用 来支持对类的子对象进行设置。通过这些接口来暴露一些功能函数,你可以尽可 能的减少一些对数据的无意修改。客户可以通过你提供的接口访问类的内部对象 ,而这个接口并不包含这个类的全部的功能。在DataSet上暴露一个IListsource 接口就是这种策略,可以阻止一些有想法的程序员来猜测实现这个接口的对象, 以及强制转换。这样做和程序员付出更多的工作以及发现更多的BUG都是自找的( 译注:这一句理解可能完全不对,读者可以自行参考原文:But programmers who go to that much work to create bugs get what they deserve.)。System.Dataset类同时也使用了最后一种策略:包装对象。 DataViewManager类提供了一种访问DataSet的方法,而且防止变向的方法来访问 DataSeto类:public class MyBusinessObject
{
// Read Only property providing access to a
// private data member:
private DataSet _ds;
public DataView this[ string tableName ]
{
get
{
return _ds.DefaultViewManager.
CreateDataView( _ds.Tables[ tableName ] );
}
}
}
// Access the dataset:
DataView list = bizObj[ "customers" ];
foreach ( DataRowView r in list )
Console.WriteLine( r[ "name" ] );