Code:
public class MyBusinessObject
{
private DataSet _ds;
public DataSet Data
{
get
{
return _ds;
}
}
}
MyBusinessObject bizObj = new MyBusinessObject();
DataSet ds = bizObj.Data;
ds.Tables.Clear(); //删除了所有的表
任何引用类型返回的都是对象的引用地址。当我们将对象的引用地址返回给调用者后,调用者就可以完全无视类的只读属性而直接操作对应地址上的引用类型。
解决方案:
四种策略可以保护类的内部成员不受到这种的威胁:使用值类型,使用不可变类型,通过接口进行功能限制和使用包装器(wrapper)。
值类型通过接口传递的是本身的拷贝。在客户程序中对拷贝的任何修改都不会反映的类的内部成员上来。客户端可以任意处理值类型的拷贝,这不会带来任何问题。
不可变类型同样是安全的,一个典型的例子就是System.String类。我们可以返回一个string或者任何其它的不可变类型,因为它们是不可被修改的。客户端的任何操作同样不会影响到类的内部成员的安全。
第三种方法是定义接口,通过接口客户程序可以使用内部成员的部分功能。当你创建类的时候,可以定义一组包含类中不同功能性的接口。通过暴露这些功能性接口,可以使内部成员暴露最小化。客户程序只能通过接口来获得我们许可的功能,而不是内部成员的全部功能。对应上例中我们可以通过只暴露DataSet类的IListsource接口的方法来避免对dataset的修改。
对于DataSet来说还有最后一种方法:使用包装器。通过DataViewManager类我们可以获得DataSet中的数据,但可以避免对其进行不必要的操作:DataViewManager可以为DataSet中的数据表创建DataView。当我们使用DataViewManager时是没有办法来修改DataSet中的数据表的内容的。我们可以对DataView进行某些操作,但是这些操作都不会反映到数据源上。
当我们希望客户端仅能查看数据时,我们可以为DateTable创建一个不可以修改的DataView来达到目的。DataView类中包含了一些属性让我们可以选择是否支持对它的添加删除修改或者存储功能。我们可以创建一个索引器来实现返回指定索引的DataView:
public class MyBusinessObject
{
private DataSet _ds;
public IList this[string tableName]
{
get
{
DataView view = _ds.DefaultViewManager.CreateDataView(_ds.Tables[tableName]);
view.AllowNew = false;
view.AllowDelete = false;
view.AllowEdit = false;
return view;
}
}
}
最后我们要将特定数据表的视图通过IList接口返回。我们可以在任何集合上使用IList接口,它是一个不可修改的对象列表;通过IList接口,我们可以保证DataView对象不会被外部操作修改。