在我们平常编程中时间久了有时候会形成一种习惯性的思维方式形成固有的编程风格但是有些地方是需要斟酌的即使是一个很小的错误也可能会导致昂贵的代价要学会善于总结从错误中汲取教训尽量不再犯同样错误注重编程之美代码的优雅总结几个平常经常犯的错误
在C#编程中字符型类型是最容易处理出错的地方代价是非常昂贵在Net Framwork中字符串是一个相当特别的引用类型string本省就是一个不可继承的密封类但是它具有了值类型所应用的特点但是它在CLR中内存还是保存于托管堆之上也就是说当我们每次定义一个字符串类型的时候就在堆内存中开辟一端内存而当我们字符串被修改之后它会创建一个新的内存注意这里的内存是不连续的而是通过修改栈内地址引用而拼凑字符串不会改变源字符串在内存中的地址所以有些程序员总是喜欢使用这样的方法格式化字符串
string SelectText=select * from +TableName+ where UserName=+Name+;
上述代码使用了字符串拼凑的方法因为使用了多重串联因此会在内存中创建两个不必要的字符串垃圾副本
其实在C#中已经为我们提供了StringBuilder和StringFromat来解决此问题虽然他们可以实现同样的功能但是他们有质的变化StringBuilder在内存中开辟的是一段连续内存当增加新字符串时候它会在栈中指向的同一个堆内存中连续存放字符这就形成了性能的提升所以我们将上面代码改成
string SelectText=stringFormat(select * from {} where UserName={}TableNameName)
大多数开发人员都不知道内置的验证数据类型的方法如SystemInt因此很多人都是自己实现的其实这是不妥的因为这些基本类型中都存在自己固有的类型验证方法下面这个就是自己实现验证的一个字符串是否是数值的代码
publicboolCheckIfNumeric(stringvalue)
{
boolIsNumeric=true;
try
{
inti=ConvertToInt(value);
}
catch(FormatExceptionexcepiton)
{
IsNumeric=false;
}
returnIsNumeric;
}
虽然使用了try catch语句这不是最佳的做法更好的方法是下面使用IntTryParse
intoutput=;
boolIsNumeric=intTryParse(valueoutoutput);
intTryParse是更快更简洁的方法
自己利用IDisposable接口手动释放内存
在NET Framework中对象的处理和使用一样重要理想的方法是在使用完对象的时候在类中实现IDisposable接口中的dispose方法进行内存的释放当然在Net本身提供的垃圾回收机制(GC)中就提供了这样的功能在我们实例化类对象时在类本身的析构函数中会调用dispose方法GC在各级内存堆满的情况下自动检查对象使用情况去相应的释放内存但是运行在非托管平台上的方法需要我们自己手动释放内存比如我们常见的SqlConnection对象也就有了下面的创建使用和处理方法
publicvoidDALOneMethod()
{
SqlConnectionconnection=null;
try
{
connection=newSqlConnection();
connectionOpen();
//sqlcommandrun
}
catch(Exceptionexception)
{
//managerexception
}
finally
{
connectionClose();
connectionDisopse();
}
}
上述代码是大部分程序员会出现的代码乍看没啥问题连接处理在最后一个代码中被明确调用但是如果发生了一个异常catch代码块就被执行然后再执行最后一个代码块处理连接因此在最后一个代码块执行之前连接将一直留在内存中大部分我们会在此处记录错误一般涉及到IO操作如果延时时间比较长的话这个连接将在内存时间长时间停留我们一个原则就是当对象不再使用的时候我们里面释放资源
我们采用程序逻辑域来处理这个问题会更好
publicvoidDALOneMethod()
{
using(SqlConnctionconnection=newSqlConnection())
{
connctionOpen()
//doSUAD
}
}
当使用using代码快时对象上的dispose()方法将在执行推出逻辑域的时候调用这样就保证了SqlConnection的资源处理被尽早释放当然这个方法也适用于实现IDisposable接口的类当时个人不推荐这样做在非常有把握的情况下可以手动释放但是没把握还是叫给net系统释放因为本身类的析构函数就实现这个方法当我们自己重写后反而会导致系统误以为你自己定义了方法而推迟释放资源有兴趣可以研究下GC运行本质假如能在第一代被释放的内存如果我们重写dispose方法反而推迟到第二代内存堆中释放显然是不可取的
学会合理的管理公共变量我们在系统中经常会滥用公共变量没有做到合适的封装好
staticvoidMain(string[]args)
{
MyAccountaccount=newMyAccount();
//这地方不能随便的调用account里面的字段进行更改但是缺改了
accountAccountNumber=ddddddddd;
ConsoleReadKey();
}
publicclassMyAccount
{
publicstringAccountNumber;
publicMyAcctount()
{
AccountNumber=ssssssssssssss;
}
}
在上面的MyAccount类中生命了一个AccountNumber公共变量理想情况下AccountNumber应该是只读的不能让外界修改但是这里MyAccount类却没有对它做任何控制
声明公共做法应该是使用属性如
publicclassMyAccount
{
privatestirng_accountNumber;
publicstringAccountNumber
{
get{return_accountNumber;}
}
publicMyAccount()
{
_accountNumber=dddddddd;
}
}
这里我们封装了AccountNumber公共变量它变成了只读不能由调用者类进行修改
嵌套的异常处理有的开发人员喜欢在方法末尾加上处理的嵌套方法如
publicclassNestedExceptionHandling
{
publicvoidMainMethod()
{
try
{
//someimplementation
ChildMethod();
}
catch(Exceptionexception)
{
//Handleexception
}
}
privatevoidChildMethod()
{
try
{
//someimplementation
ChildMethod();
}
catch(Exceptionexception)
{
//Handleexception
throw;
}
}
privatevoidChildMethod()
{
try
{
//someimplementation
}
catch(Exceptionexception)
{
//Handleexception
throw;
}
}
}
如果相同的异常被处理多次性能开销将会增加
我们的解决方法是让异常处理方法独立开来如
publicclassNestedExceptionHandling
{
publicvoidMainMethod()
{
try
{
//someimplementation
ChildMethod();
}
catch(Exceptionexception)
{
//Handleexception
}
}
privatevoidChildMethod()
{
//someimplementation
ChildMethod();
}
privatevoidChildMethod()
{
//someimplementation
}
}
大数据量上使用Dataset和DataReader混用当单表数据量很大的情况使用DataSet是一种很不明智的选择应为DataSet是以DataTable内存形式存放数据量一次性将数据拖入内存当数据很大的情况下这种方式是很吃内存的相比DataSerDataReader就显得优雅很多它是每次读取一条数据然后轮询调用机制但是也有它的弊端就是相对长连接但是对内存消耗而言这是有利的当然DataSet在大部分应用场景下也是有自己的优点充分解耦一次性操作领域模型操作等方面两者分情况分场景而用这里只是稍微提提根据场景分析区别