Effective C# 原则41:选择DataSet而不是自定义的数据结构
2009-02-19 08:17:41 来源:WEB开发网为了在我们自己定义的集合上也实现这样的概念,我们很要做点工作。你要修改你的AddressRecord 结构来支持两个新的接口,IEditableObject 和IDataErrorInfo。IEditableObject 为你的类型提供了对事务的支持。IDataErrorInfo 提供了常规的错误处理。为了支持事务,你必须修改你的数据存储来提供你自己的回滚功能。你可能在多个列上有错误,因此你的存储必须包含一个包含了每个列的错误集合。这是一个为AddressRecord做的更新的列表:
public class AddressRecord : IEditableObject, IDataErrorInfo
{
private struct AddressRecordData
{
public string street;
public string city;
public string state;
public string zip;
}
private AddressRecordData permanentRecord;
private AddressRecordData tempRecord;
private bool _inEdit = false;
private IList _container;
private Hashtable errors = new Hashtable();
public AddressRecord( AddressList container )
{
_container = container;
}
public string Street
{
get
{
return ( _inEdit ) ? tempRecord.street :
permanentRecord.street;
}
set
{
if ( value.Length == 0 )
errors[ "Street" ] = "Street cannot be empty";
else
{
errors.Remove( "Street" );
}
if ( _inEdit )
tempRecord.street = value;
else
{
permanentRecord.street = value;
int index = _container.IndexOf( this );
_container[ index ] = this;
}
}
}
public string City
{
get
{
return ( _inEdit ) ? tempRecord.city :
permanentRecord.city;
}
set
{
if ( value.Length == 0 )
errors[ "City" ] = "City cannot be empty";
else
{
errors.Remove( "City" );
}
if ( _inEdit )
tempRecord.city = value;
else
{
permanentRecord.city = value;
int index = _container.IndexOf( this );
_container[ index ] = this;
}
}
}
public string State
{
get
{
return ( _inEdit ) ? tempRecord.state :
permanentRecord.state;
}
set
{
if ( value.Length == 0 )
errors[ "State" ] = "City cannot be empty";
else
{
errors.Remove( "State" );
}
if ( _inEdit )
tempRecord.state = value;
else
{
permanentRecord.state = value;
int index = _container.IndexOf( this );
_container[ index ] = this;
}
}
}
public string Zip
{
get
{
return ( _inEdit ) ? tempRecord.zip :
permanentRecord.zip;
}
set
{
if ( value.Length == 0 )
errors["Zip"] = "Zip cannot be empty";
else
{
errors.Remove ( "Zip" );
}
if ( _inEdit )
tempRecord.zip = value;
else
{
permanentRecord.zip = value;
int index = _container.IndexOf( this );
_container[ index ] = this;
}
}
}
public void BeginEdit( )
{
if ( ( ! _inEdit ) && ( errors.Count == 0 ) )
tempRecord = permanentRecord;
_inEdit = true;
}
public void EndEdit( )
{
// Can't end editing if there are errors:
if ( errors.Count > 0 )
return;
if ( _inEdit )
permanentRecord = tempRecord;
_inEdit = false;
}
public void CancelEdit( )
{
errors.Clear( );
_inEdit = false;
}
public string this[string columnName]
{
get
{
string val = errors[ columnName ] as string;
if ( val != null )
return val;
else
return null;
}
}
public string Error
{
get
{
if ( errors.Count > 0 )
{
System.Text.StringBuilder errString = new
System.Text.StringBuilder();
foreach ( string s in errors.Keys )
{
errString.Append( s );
errString.Append( ", " );
}
errString.Append( "Have errors" );
return errString.ToString( );
}
else
return "";
}
}
}
花了几页的代码来支持一些已经在DataSet里实现的了的功能。实际上,这还不能像DataSet那样恰当的工作。例如,交互式的添加一个新记录到集合中,以及支持事务所要求的BeginEdit, CancelEdit, 和EndEdit等。 你要在CancelEdit 调用时检测一个新的对象而不是一个已经修改了的对象。CancelEdit 必须从集合上移除这个新的对象,该对象应该是上次调用BeginEdit时创建的。对于AddressRecord 来说,还有很多修改要完成,而且一对事件还要添加到AddressList 类上。
最后,就是这个IBindingList接口。这个接口至少包含了20个方法和属性,用于控件查询列表上的功能描述。你必须为只读列表实现IBindingList 或者交互排序,或者支持搜索。在你取得内容之前就陷于层次关系和导航关系中了。我也不准备为上面所有的代码添加任何例子了。
几页过后,再问问你自己,还准备创建你自己的特殊集合吗?或者你想使用一个DataSet吗?除非你的集合是一个基于某些算法,对性能要求严格的集合,或者必须有轻便的格式,就要使用自己的DataSet,特别是类型化的DataSet。这将花去你大量的时间,是的,你可以争辩说DataSet并不是一个基于面向对象设计的最好的例子。类型化的DataSet甚至会破坏更多的规则。但,使用它所产生的代码开发效率,比起自己手写更优美的代码所花的时间,这只是其中一小部份。
- ››选择好的广告联盟:选择广告联盟理解掌握的六大绝招...
- ››选择谁? 揭秘90后必备的音乐播放器
- ››选择性关闭Win 7视频预览 节约系统资源
- ››选择适合的SRAM存储器的技巧
- ››Effective C# 原则40:根据需求选择集合
- ››Effective C# 原则41:选择DataSet而不是自定义的...
- ››Effective C# 原则42:使用特性进行简单的反射
- ››Effective C# 原则43:请勿滥用反射
- ››Effective C# 原则44:创建应用程序特定的异常类
- ››Effective C# 第6章:杂项
- ››Effective C# 原则45:选择强异常来保护程序
- ››Effective C# 原则47:选择安全的代码
更多精彩
赞助商链接