LINQ里的“与equals的区别”和“==”的区别

------解决方案--------------------外部定义个字典, key: m.StateNum
value: htmlDictionary&int,string& dic=new Dictionary&int,string&();
dic.Add(5,"&a
href='CheckBillsOfLading.aspx?id={0}' &校验提货单&&&/a&");
然后:DIS =dic.ContainsKey(m.StateNum)? string.Format(dic[m.StateNum],m.ID):"";
velocity模板点染页面性能优化
velocity模板渲染页面性能优化Velocity模板渲染页面性能优化
对于页面性能优化这块,尤其是velocity宏的使用,如何使用宏,怎么样将宏的使用发挥到极致,达到更大的性能的提示,我的建议如下:1:一般我们不推荐使用宏,因为宏每次都要要JJT解析,然后才能再执行;2:使用宏能达到最好的性能情况下,非常安全的,一般在页面有些信息是用户输入的情况使用会比较好。下面我针对他的一些意见和我们项目的一些情况,整理了一些关于宏使用的一些技术知识一:我们如何定义宏和使用宏?1:定义宏和使用宏#macro指令用于定义一个VTL模板的重复代码块——宏。下面是一个简单的定义宏的例子:#macro( d )&tr&&td&&/td&&tr&#end这段代码定义了一个宏,名字为d,没有参数。下面是使用这个宏的代码:#d()Velocity在遇到#d()的时候,会用"&tr&&td&&/td&&/tr&"替代上面的#d()这一行。宏的参数:宏也可以带参数,而且是任意多个参数。不过,宏定义时有几个参数,宏调用时就要提供同样数目的参数。#macro( d $name)
&tr&&td&$name&/td&&/tr&#end#d("name1")宏的参数可以是以下VTL元素中的任意一种:引用、字符串字面值、数值字面值、整数范围(比如[1 .. 10]、[$start .. $end])、数组、布尔值true或者false。宏的参数可以是方法,那么下面这个例子,需要特别注意:#macro(test $a)
$a $a $a#end#test($foo.bar())上面这个例子中,$foo.bar()将会被调用3次,而不是一次。内联的宏当宏是在一个Velocity模板中定义时,这个宏(是inline的)只能被该模板使用,同一个网站下的其他模板是不能用的。如果是在一个Velocity宏模板库中定义的宏,就可以被任何同一网站下的模板使用。和宏有关的一些Velocity属性velocimacro.library——用逗号分隔的一组文件名,是Velocity宏模板库。默认值是VM_global_library.vmvelocimacro.permissions.allow.inline——宏是否可以在一个普通模板中定义。默认值是false。velocimacro.permissions.allow.inline.to.replace.global——是否允许模板中的宏覆盖library中的宏。默认值是false。velocimacro.permissions.allow.inline.local.scope——一个在普通模板中定义的宏,是否允许其他模板使用。默认是false。velocimacro.context.localscope——在一个宏里通过#set()修改了context,此修改是否仅仅对这个宏自身,而不是永久性修改了context。默认值是false。velocimacro.library.autoreload——Velocity宏模板库修改之后,是否自动重新加载。默认值是false。debug时可以设置为true,发布时设置为false。其他一些注意点宏必须在第一次使用它之前定义。当#Parse()一个模板文件时,尤其要注意这一点。二: 引入指令和#Stop指令#Include和#Parse都是用于将本地文件引入当前文件的指令,而且被引入的文件必须位于TEMPLATE_ROOT。这两者之间有一些区别。#Include被#Include引入的文件,其内容不会被Velocity引擎解析,所以这些文件应该是静态模板,即不含有VTL的模板。使用#Include()指令时,参数是被双引号括起来的文件名或者是表示文件名的变量。如果有多个文件,以逗号隔开即可。比如#Include("a.gif", "b.html", $file)。#Parse#Parse用来在当前模板中引入并执行另一个(本地的)模板——可以是静态的,也可以是动态的——并把结果嵌入到当前位置。#Parse()指令的参数,可以是一个双引号括起来的文件名,也可以是一个变量,但是它不能接受多个参数。被#Parse引入的文件仍然可以使用#Parse指令。在velocity.properties文件中有一个属性directive.parse.max.depth,默认值是10,它指定了#Parse嵌套的最大层次。既然#Parse嵌套是允许的,#Parse递归也是允许的。假如a.vm #Parse b.vm,那么a.vm中定义的变量$v,在b.vm中可以随便使用。如果b.vm也定义了$v,那么b.vm中用到的将会是自己的$v,而不是a.vm中的$v。#Stop#Stop指令会停止模板引擎的执行,并返回。这在debug中比较有用。三:#include和#parse到底有什么区别?parse方法是解析文件,宏是全局方法(调用该方法返回数据),调用方法比较快。#parse是动态加载的,需要***.vm中的宏定义在 velocity启动的时候就应该得到加载#include1.可包含本地文件(不包含VTL)2.文件内容不经过template engine处理3.出于安全性的考虑,此文件只能位于TEMPLATE_ROOT目录下#parse1.可以引入包含VTL的模板2.任何模板文件只能位于TEMPLATE_ROOT目录下比如macro.vm里面添加#macro (static $src)
$env.getProperties("app.static")/$src#end#macro (function_name param_names)
// code...#end这样使用 #static("...")方法调用parse与include的区别在于,若包含的文件中有Velocity脚本标签,将会进一步解析,而include将原样显示。实例1:#macro(macroName)#end 脚本函数(宏)调用,不推荐在界面模板中大量使用。如:在使用EasyJWeb Tools快速生成的添删改查示例中,可以点击列表的标题栏进行升降排序显示,这是我们在EasyJWeb应用中经常看到的一个排序状态显示的模板内容。 函数(宏)定义,一般放在最前面 #macro(orderPic $type) #if ($orderField.equals($type)) &img src="/images/ico/${orderType}.gif"& #end #end具体的调用如:&font color="#FFFFFF"&头衔#orderPic("title")&/font&实例2:包含文件#inclue("模板文件名")或#parse("模板文件名")主要用于处理具有相同内容的页面,比如每个网站的顶部或尾部内容。使用方法,可以参考EasyJF开源Blog及EasyJF开源论坛中的应用!如:#parse("/blog/top.html")或#include("/blog/top.html")四: Velocity宏使用中需要注意的问题1、变量输出要带上静止修饰符,避免模板直接空指针报错举例 :$var错误,$!var正确如果变量referce与html代码连在一起,需要加上{}举例 :$! {var}gadsgaggaggas2、Macro的使用由于宏存在着安全和性能问题,并且会影响模板的可读性,在模板中不推荐使用宏。 3、变量引用不要加引号,避免重复转义举例:$stringUtil.equals("$text", "&") 错误$stringUtil.equals($text, "&") 正确4、关于velocity的版本目前推荐使用 velocity 1.6.1,因为以往velocity版本往往会出现向下不兼容的情况 版本升级需要经过架构部门(校长)的充分论证和评估才能实施。5、Velocity的安全性为避免VELOCITY页面渲染后产生XSS漏洞,应用应该采用校长的安全方案在应用中引入toolkit-service-velocity.1.5 包。XSS的过滤,使用velocity提供的xss过滤功能1)对于html,可以使用
#SHTML($html)2)对于JS,可以用 #SJS($js)3)对于xml,可以使用#SXML($xml)4)屏蔽转义,可以使用#SLITERAL() 上述宏,因为非常消耗CPU,因此如果不含特定内容,不到万不得已,不得使用。五:总结:1:模块化的东西最好不要放入循环当中,parse或者宏都只是加载一段公共的代码片段而已,循环只是对数据处理来说的;2:用parse和宏区别不大,parse解析的是一个vm文件,宏返回一段vm代码,优化的过程不是说使用parse或者宏,而是文件或宏里面的数据处理代码,这些代码才有高低效率之分,parse和宏只是在不同场景下不同的使用方法而已。参考文章:http://people.apache.org/~henning/velocity/html/ch07.html
怎样快速得到大文件的大小?该怎么解决
怎样快速得到大文件的大小?曾用filestream和文件映射但好像都比较慢啊?
读一个500M以上的文件要好久。
有什么快速的方法吗?我的环境是B/S结构的。------解决方案--------------------是上传慢,而不是判断慢,用什么办法都没用!
因为服务器端代码要在服务器端判断,必然要全部上传
你可以试一下,用客户端javascript判断
------解决方案--------------------如果是服务器端本来存在的文件,
实例化一个FileInfo对象即可
FileInfo fi = new FileInfo(path);
int size = fi.L
------解决方案--------------------500M,可以把你机子的内存都占满了,当然要慢了!
如果您喜欢IT行业或者对IT行业感兴趣,想开拓技术视野,欢迎加入本站官方QQ群:,在群里认识新朋友和交流技术^_^
本站联系邮箱:C# 程序员易犯的 10 个错误 - 博客 - 伯乐在线
& C# 程序员易犯的 10 个错误
C#是针对微软公共语言运行库(CLR)的开发语言之一。针对CLR的开发语言得益于如跨语言集成的性能,异常处理,安全性增强,组件交互的简化模型,调试和分析服务。对于今日的CLR来说,C#是定位到Windows桌面,移动设备或服务器环境中,在处理复杂,专业的开发项目方面使用最广泛的开发语言。
C#是面相对象,强类型的语言。C#中严格的类型检查,在编译和运行时,使得典型的编程错误能尽早报告,并且能精准给出错误位置。这能帮助程序员节省很多时间,相比于跟踪那些可以发生在违规操作很长时间之后的令人费解的错误,类型安全的执行更加自由。但是,许多程序员不知不觉地(或不经意地)丢弃了这种检测的好处,引出了一些在本文中讨论的问题。
本文描述了C#程序员最常见的10个编程错误或者要避免的缺陷。
大多数在本文中讨论的错误是特定于C#的,有些也涉及到CLR或者利用框架类库(FCL)的其他语言。
常见错误1:像值类型一样使用引用类型或者相反
C++程序员,和其他许多编程语言的程序员,习惯于把他们分配给变量的是否只是值或者对已存在对象的引用置于掌控之中。但在C#中,这个是由写这个对象的程序员,而不是由实例化对象并给它赋值的程序员决定的。这对于C#新手来说是一个常见的“骗到你了”的实例。
如果你不知道你使用的对象是值类型还是引用类型,你可能会碰到惊喜。例如:
Point point1 = new Point(20, 30);
Point point2 = point1;
point2.X = 50;
Console.WriteLine(point1.X);
// 20 (does this surprise you )
Console.WriteLine(point2.X);
Pen pen1 = new Pen(Color.Black);
Pen pen2 = pen1;
pen2.Color = Color.B
Console.WriteLine(pen1.Color);
// Blue (or does this surprise you )
Console.WriteLine(pen2.Color);
正如你所看到的, Point 和 Pen 对象是以完全相同的方式创建的。但 point1 在一个新的 X 坐标值设置到 point2后保持不变,而 pen1 的值在一个新的颜色值设置到 pen2后被改变了。因此我们可以推断 point1 和 point2 分别包含 Point 对象的拷贝,而 pen1 和 pen2 包含对同一个 Pen 对象的引用。但我们怎么能不通过这个实验而知道结果呢?
答案是查看对象类型的定义(在Visual Studio中你可以很容易得把你的光标放在对象类型名上并点击F12):
public struct Point { … }
// defines a “value” type
public class Pen { … }
// defines a “reference” type
正如上面所示,在C#中,关键字struct是用于定义值类型,同时关键字class是用来定义引用类型的。对于有C++编程背景的程序员,对C++和C#有许多相似的关键字而误认为安全的话,这种行为可能会是一个惊喜。
如果你要依赖某些因值类型和引用类型的不同而产生的行为—-比如传递一个对象作为方法参数并且在方法中改变该对象的状态这种行为—一定要确保你处理的是正确的对象类型。
常见错误2:误解未初始化变量的默认值
在C#中,值类型不能为null。通过定义,值类型会有一个值,甚至没有初始化的值类型变量也必须有个值。这称为值类型的默认值。这会导致当检查一个变量是否初始化时不可预期的结果,如下所示:
class Program {
static Point point1;
static Pen pen1;
static void Main(string[] args) {
Console.WriteLine(pen1 == null);
Console.WriteLine(point1 == null);
// False (huh )
为什么 point1 不是null?答案是 Point 是值类型,并且 Point 的默认值是(0,0),而不是null。没有认识到这点是C#中一个易犯(并且常见)的错误。
许多(但不是全部)值类型都有一个 IsEmpty 属性,你可以用这个属性来检查该值类型是否等于它的默认值:
Console.WriteLine(point1.IsEmpty);
当你去检查一个变量是否被初始化,确保你知道那个类型未被初始化的变量将有的默认值并且不依赖它为null。
常见错误3:使用不合适或者未特别指定的字符串比较方法
在C#中有许多不同的方法比较字符串。
尽管许多程序员用 == 操作符来比较字符串,但其实这是许多方法中最不理想的方法之一,主要是因为它在代码中没有明确指明需要哪一种比较。
相反,在C#中测试字符串想等的首选方式是使用 Equals 方法:
public bool Equals(string value);
public bool Equals(string value, StringComparison comparisonType);
第一个方法的签名(例如,没有 comparisonType 参数),实际上和使用 == 操作符完全一样,但具有对字符串明确化的好处。它执行一个字符串的序号比较,基本上就是字节与字节比较。在许多情况下,这正是你想要的比较类型,特别是当比较的字符串的值参数化,例如文件名,环境变量,属性等等。在这种情况下,只要顺序比较的确是这种情况下正确的类型比较即可,唯一的缺点是使用没有 comparisonType 的 Equals 方法,会使得某些读你代码的人不知道你用什么比较类型做的比较。
使用带 comparisonType 参数的 Equals 方法,你每次比较字符串的时候,虽说,不光会使得你的代码更清晰,而且会使你明确你需要使用的比较类型。这是值得做的事情,因为尽管英语在按顺序比较与语言区域性比较之间没什么差异,但其他语言提供了很多,而忽略其他语言的可能性则为你自己在未来的路上提供了犯很多错误的可能。例如:
string s = &strasse&;
// outputs False:
Console.WriteLine(s == &strasse&);
Console.WriteLine(s.Equals(&strasse&));
Console.WriteLine(s.Equals(&strasse&, StringComparison.Ordinal));
Console.WriteLine(s.Equals(&Strasse&, StringComparison.CurrentCulture));
Console.WriteLine(s.Equals(&strasse&, StringComparison.OrdinalIgnoreCase));
// outputs True:
Console.WriteLine(s.Equals(&strasse&, StringComparison.CurrentCulture));
Console.WriteLine(s.Equals(&Strasse&, StringComparison.CurrentCultureIgnoreCase));
最安全的实践是总是为 Equals 方法提供一个 comparisonType 参数。这是一些基本准则:
当比较有用户输入的字符串,或者将显示给用户的字符串,使用本地化比较(CurrentCulture 或者 CurrentCultureIgnoreCase)。
当比较程序设计用的字符串,使用原始比较(Ordinal 或者 OrdinalIgnoreCase)。
InvariantCulture 和 InvariantCultureIgnoreCase 通常并不使用,除非在受限的条件下,因为原始比较更加有效。如果本地性文化比较是必须的话,它应该基于当前文化或另一个明确的文化来执行。
此外,对于 Equals 方法来说,字符串也提供了 Compare 方法,用来给你提供关于字符串相对顺序信息而不仅仅测试是否相等。这个方法更适用 &, &=, & 和&= 操作符,与上文讨论的原因相同。
常见错误4:使用迭代(而不是声明)来操作集合
在C# 3.0中,LINQ()的引入永远改变了集合的查询和修改操作。自那以后,当你使用迭代式操作集合,而不是使用LINQ的时候,其实你也许应该使用后者。
一些C#程序员甚至不知道LINQ的存在,但庆幸的是这个数字正在逐步减少。但因为LINQ的关键字和SQL的语句的相似性,很多人还是误以为LINQ只用于数据库的查询中。
虽然数据库的查询操作是LINQ的一个非常常用的功能,但是它同样适用于各种枚举的集合(例如,任何实现了IEnumerable 接口的对象)。举例来说,如果你有一个Accounts类型的数组,不要写成:
decimal total = 0;
foreach (Account account in myAccounts) {
if (account.Status == &active&) {
total += account.B
你只要写成:
decimal total = (from account in myAccounts
where account.Status == &active&
select account.Balance).Sum();
虽是一个简单的例子,在有些情况下,一个单一的LINQ语句可以轻易地替换你代码中一个迭代循环(或嵌套循环)里的几十条语句。更少的代码通常意味着更少产生bugs的机会。然而,记住在性能方面可能要权衡一下。在性能决定的情况下,特别是当你的迭代代码能对你的集合进行假设而LINQ做不到的时候,确保在两种方法间做一个性能比较。
常见错误5:没有考虑LINQ语句中底层对象
对于处理抽象操作集合LINQ是强大的,无论它们是内存的对象,数据库表,或者XML文档。在完美的世界中,你无须考虑底层对象是什么。但这里的错误是假设我们生活在一个完美的世界中。事实上,当用完全相同的数据时相同的LINQ语句能返回不同的结果,如果这个数据以不同的格式给出的话。
例如,考虑如下语句:
decimal total = (from account in myAccounts
where account.Status == &active&
select account.Balance).Sum();
如果其中一个对象的 account.Status 等于 “Active”(注意大写A)会发生什么?好,如果myAccounts 是一个 DbSet 的对象(默认设置了不区分大小写的配置), where 表达式仍会匹配该元素。但是,如果 myAccounts 是在内存中的数组,那么它将不匹配,并将产生不同的总的结果。
等一下,在我们之前讨论字符串比较过程中,我们发现 == 操作符进行了字符串的顺序比较。那么,为什么在这个条件下, == 操作符表现出不区分大小写的比较呢?
答案是,当在LINQ语句中的底层对象都引用到SQL表中的数据(如在这个例子中,实体框架为DbSet对象的情况下),该语句被转换为一个T-SQL语句。操作符遵循T-SQL的规则,而不是C#的,所以在上述情况的比较中不区分大小写。
通常来说,尽管LINQ是一个有用的和以持续的方式查询对象的集合,但在现实中你仍然需要知道你的语句是否会被解释成顶着C#的帽子的其他类型的语句,以确保你代码的功能在运行时仍如预期的那样。
常见错误6:对扩展方法感到困惑或被欺骗
正如之前提到的,LINQ语句依赖于任何实现了IEnumerable 接口的对象。比如,下面的简单函数将账户上任何集合的余额相加:
public decimal SumAccounts(IEnumerable&Account& myAccounts) {
return myAccounts.Sum(a =& a.Balance);
在上面的代码中,myAccounts参数的类型被声明为IEnumerable&Account&。myAccounts 引用了一个 Sum 方法(C#使用类似”dot notation”引用类或者接口中的方法),我们期望在 IEnumerable&T& 接口中定义一个 Sum() 方法。但是, IEnumerable&T& 没有为 Sum 方法提供任何引用并且只有如下所示的简单定义:
public interface IEnumerable&out T& : IEnumerable {
IEnumerator&T& GetEnumerator();
那么 Sum() 方法在哪里定义的呢?C#是强类型语言,因此如果 Sum 方法的引用是无效的,C#编译器就会将其标示为错误。我们知道它必须存在,但是在哪呢?此外,LINQ提供的供查询和聚集集合的所有方法定义在哪里呢?
答案是 Sum() 并不是定义在 IEnumerable 接口内的方法。而是一个定义在 System.Linq.Enumerable 类中的静态方法(叫做”扩展方法”):
namespace System.Linq {
public static class Enumerable {
// the reference here to “this IEnumerable&TSource& source” is
// the magic sauce that provides access to the extension method Sum
public static decimal Sum&TSource&(this IEnumerable&TSource& source,
Func&TSource, decimal& selector);
那么扩展方法和其他静态方法有什么不同之处,是什么确保我们可以在其他类访问它呢?
扩展方法的显著特点是,第一个形参前的 this 修饰符。这就是编译器知道它是一个扩展方法的“奥妙”。它所修饰的参数类型(在这里是IEnumerable&TSource&)说明这个类或者接口将会实现这个方法。
(另外需要说明的一点,定义扩展方法的IEnumerable 接口和Enumerable 类的名字间的相似性并没有什么可奇怪的。这种相似性仅是随意的风格。)
理解了这一点后,我们可以看到上面介绍的 sumAccounts 方法可以用下面的方法来实现:
public decimal SumAccounts(IEnumerable&Account& myAccounts) {
return Enumerable.Sum(myAccounts, a =& a.Balance);
事实是我们可能已经以这种方式实现它了,而不是问为什么要有扩展方法呢?本质上是C#语言的一种便利方式,它允许你对已存在的类型“添加”方法,而无须创建一个新的派生类型,重新编译或者修改原类型代码。
扩展方法通过在文件头部添加 using [namespace]; 而引入作用域。你需要知道你寻找的扩展方法所在的命名空间,但一旦你知道你要找什么的时候这就变得非常简单了。
当C#编译器遇到一个对象实例调用一个方法,并且该方法没有定义在引用对象类中时,它就会搜寻所有定义在作用域中的扩展方法以寻找相匹配的方法签名和类。如果它找到了,它就会把实例的引用作为第一个参数传给扩展方法,如果有其他参数的话,再把它们传递给扩展方法。(如果C#编译器在作用域中没有找到任何相符合的扩展方法,它就会抛出异常。)
对于C#编译器来说,扩展方法是个“语法糖”,(大多数情况下)使得我们把代码写得更清晰,更易于维护。显然,前提是你知道它们的用法。否则,它会让人感觉比较困惑,尤其是一开始的时候。
使用扩展方法确实有优势,但也让不了解它或者不能很好理解它的开发者感到头疼,还浪费时间。尤其是看网上代码示例的时候,或者任何其它事先写好的代码的时候。当这些代码发生编译错误(因为它调用了显然没被定义在调用类中的方法),倾向是认为代码是否应用于类库的不同版本,或者是不同的类库。很多时间都会花在寻找新版本上,或者被认为“丢失”的类库上。
在扩展方法的名字和类中的名字一样,但只是在方法签名上有微小差异的时候,即使对扩展方法熟悉的开发者偶尔也可能犯上面的错误。很多时间会花在寻找不存在的类型或者错误上。
使用C#类库的扩展方法变得越来越普遍了。除了LINQ,另外两个出自微软被广泛使用的类库和也应用了扩展方法,并且还有其它也应用的。框架越新,它就越可能使用扩展方法。
当然你也可以写自己的扩展方法。但必须意识到扩展方法看上去和其它实例方法一样,但这只是假象。事实上,你的扩展方法不能引用它扩展的类的私有成员变量或者保护成员变量,并且不能被当做传统类的完全替代品。
常见错误7:对手上的任务使用错误的集合类型
C#提供了大量的集合对象,下面只列出其中的一部分:
Array, ArrayList, BitArray, BitVector32, Dictionary&K,V&, HashTable, HybridDictionary, List&T&,NameValueCollection, OrderedDictionary, Queue, Queue&T&, SortedList, Stack, Stack&T&, StringCollection,StringDictionary
有这样的情况,太多的选择和没有选择一样糟糕。但这种情况不适用于集合对象。数量众多的选择当然对你有益。花一些时间提前研究一下集合类型,以便选择一个你需要的集合类型。这样可能性能更好,更少出错。
如果有一个集合类型和你操作的类型一样(String或bit),你最好使用它。当指定具体的元素类型时,集合更有效率。
为了利用C#类型安全特性,通常你应该选择泛型接口而不是非泛型的。泛型接口的元素是当你声明对象的时候指定的类型,而非泛型接口中的元素是对象类型的。当使用非泛型接口时,C#编译器不能对你的代码做类型检查。同样,当你操作原生类型集合的时候,使用非泛型接口会导致对这些类型频繁得进行操作,和使用了合适类型的泛型集合相比,这么做会带来明显的负面的性能影响。
另一个常见的陷阱是你自己创建集合对象。并不是说永远不要这么做,但是和.NET提供的广泛使用的集合类型相比,通过使用或扩展已存在的集合类型,你可能节省下大量的时间,胜于重复造轮子。特别是,C#的和CLI提供了很多额外的集合类型,例如持久化树形数据结构,基于堆的优先级队列,哈希索引的数组列表,链表和更多。
常见错误8:忽略资源释放
CLR运行环境才用一个垃圾收集器,所以你不要显式释放已创建的任何对象所占用的内存。事实上,你也不能这么做。C#中没有和C++delete对应的运算符或者C中free()对应的方法。但这并不意味着在你可以忽略所有你使用过的对象。许多对象类型封装了一些其他类型的系统资源(例如,磁盘文件,数据连接,网络端口等等)。保持这些资源处于使用状态会很快耗尽系统资源,降低性能并最终导致程序出错。
虽然析构方法可以定义在任何一个C#的类中,但是析构方法(C#中也叫终结器)的问题是你不能确定他们什么时候将被调用。在未来一个不确定的时间它们被垃圾回收器调用(在一个异步线程中,可能会引发额外的并发)。试图避免这种由垃圾回收器所强制调用的 GC.Collect() 并不是一个好的实践,因为在垃圾回收器回收适合回收的对象时,这么做会导致在不可预知的时间内阻塞进程。
这并不是使用终结器没好处,但显式得释放资源并不是其中之一。更确切地说,当你操作文件,网络端口或者数据库连接的时候,当你不再使用它们的时,你应该显式释放这些资源。
资源泄露在几乎任何环境中都会引起关注。但是,C#使用了一种健壮的机制,使得资源的使用变得简单,如果使用合理的话,会使资源泄露极少发生。.NET框架定义了IDisposable 接口,仅由Dispose() 构成。任何实现了IDisposable接口的对象都会在对象生命周期结束之后调用析构方法。这会显式得,确定得释放资源。
如果在一段代码中创建并释放对象,忘记调用Dispose()是不可原谅的,因为C#提供了一个 using 语句以确保 Dispose() 被调用而不论代码块以什么方式退出(不管是异常,是返回值,或是简单的代码块结束)。没错,这是和之前文中提到的在你文件的头部引入命名空间一样的 using 语句。它有一个许多c#开发者没有察觉到的,完全不相关的目的,也就是当代码块退出的时候确保 Dispose() 被调用。
using (FileStream myFile = File.OpenRead(&foo.txt&)) {
myFile.Read(buffer, 0, 100);
在上面的创建 using 代码示例中,你可以确定一旦你处理完文件之后, myFile.Dispose() 方法会被调用,不论 Read() 方法是否抛出异常。
常见错误9:回避异常
C#在运行时也会强制类型安全检查。比起像C++这样会因错误类型转换而赋给对象的域一个随机值的语言来说,C#让你更快得找出错误的位置。然而,程序员再一次忽视了C#的这种特性。由于C#提供了两种不同的类型检查方式,一种会抛出异常而另一种不会,从而导致他们掉进这个陷阱。有些人选择回避抛异常这种方式,想着不去写try/catch语句块可以节省一些代码。
例如,这里演示了C#在显式类型转换中两种不同的方式:
// METHOD 1:
// Throws an exception if account can't be cast to SavingsAccount
SavingsAccount savingsAccount = (SavingsAccount)
// METHOD 2:
// Does NOT throw an exception if account can't be cast to
// SavingsA will just set savingsAccount to null instead
SavingsAccount savingsAccount = account as SavingsA
方法2中可能发生的最明显错误是对返回值类型检查的失败。这最终很可能导致NullReferenceException的异常,这可能出现在稍晚的时候,使得追踪问题根源变得更加困难。相比之下,方法1会立即抛出一个 InvalidCastException 异常,使得问题根源十分明显。
此外,即使你知道要检查方法2 的返回值,那么如果你发现值为空你会怎么做?在这个方法中报出错误合适吗?如果类型转换失败你还能尝试着做什么?如果不能,那么抛出异常是正确的选择,并且异常的抛出点离问题根源越近越好。
这里演示了另外一组常见的方法,其中一种会抛出异常,另一种不会:
int.Parse();
// throws exception if argument can’t be parsed
int.TryParse();
// returns a bool to denote whether parse succeeded
IEnumerable.First();
// throws exception if sequence is empty
IEnumerable.FirstOrDefault();
// returns null/default value if sequence is empty
有些程序员认为“异常不利”,从而他们自然得认为不抛异常的方法是极好的。虽然在某些情况下,这种观点是正确的,但是它并不适用于普遍的情况。
举个具体的例子,在某种情况下当异常发生时你有一个可选的合理的措施(比如,默认值),那么不抛出异常将是一个合理的选择。这种情况下,最好像下面这么写:
if (int.TryParse(myString, out myInt)) {
// use myInt
// use default value
用来替代:
myInt = int.Parse(myString);
// use myInt
} catch (FormatException) {
// use default value
然而,这并不说明 TryParse 方法更好,某些情况下适合,某些情况下不适合。这就是为什么有两种选择。在你的上下文中使用正确的方法,并记住作为程序员,异常无疑可以成为你的朋友。
常见错误10:允许编译器警告累积
虽说这并不是C#特有的,但它弃用了由C#编译器提供的严格类型检查的优势,这是非常过分的。
警告的出现是有原因的。所有的C#编译器错误表明你的代码有缺陷,许多警告同样也表明这个问题。两者的区别是,对警告来说,编译器可以按照你的代码指示工作。即便如此,如果编译器发现你的代码有一点可疑,那么很可能你的代码不会完全按照你的预期运行。
一个常见的简单例子是当你修改你的算法并删除了你之前使用的变量时,但是你忘了删除变量的声明。程序可以很好地执行,但是编译器将会标示无用的变量声明。程序完美运行的事实使得程序员忽视了修正警告。再者,程序员利用了Visual Studio的特性,使得他们很容易得在“错误列表”窗口中隐藏了警告,从而只关注错误信息。用不了多久就会积累许多警告,所有这些警告都被欢乐得忽略了(或更糟糕的,隐藏了)。
但如果你忽略这种警告,迟早类似下面的例子会出现在你的代码里:
class Account {
// compiler warned you about this, but you didn’t listen!
// Constructor
Account(int id) {
this.myId = Id;
伴随着我们编码时编译器的快速智能提示,这种错误很可能发生。
现在你的程序里有了一个严重的错误(尽管编译器只将其标示为警告,原因已经解释过了),你可能花大量时间找出这个问题,这取决于你程序的复杂度。如果你一开始就注意到这个警告,那么你仅需5秒钟就可以修正它并避免这个问题。
记住,C#编译器对于你程序的健壮性,提出了许多有用的信息。如果你在听。不要忽视警告。它们通常仅需要花几秒钟去修正,当出现新的警告时就修正它,会为你节省很多时间。训练你自己以期待Visual Studio的“错误窗口”显示“0错误,0警告”,以至于一旦出现任何警告,你都会感觉不舒服而立刻把警告修正。
当然,每个规则都有例外。因此,有这样的情况,就是你的代码在编译器看来有点可疑,即使它们是完全按照你的意图去完成的。在这种极少数的情况下,仅在触发警告的代码上使用#pragma warning disable [warning id] ,并且仅针对其触发的警告ID。这样会压制这条警告,以便当新的警告出现时,你还可以获得新的警告的提示。
C#是一门强大并且灵活的语言,它有很多机制和规范用来显著提升效率。相比任何一种软件工具或者语言,如果对其能力只有有限了解或者认识,有时可能更多的是阻碍而不是好处,正如一句谚语所说“自以为知道很多,能够做某事了,其实不然”。
熟悉C#的一些细微关键之处,如本文中提到的问题(但不限于),将会有助于我们更好地使用语言,从而避免更多的易犯错误。
关于作者:
可能感兴趣的话题
Equals优于==?屁。
o 129 回复
o 163 回复
o 112 回复
关于伯乐在线博客
在这个信息爆炸的时代,人们已然被大量、快速并且简短的信息所包围。然而,我们相信:过多“快餐”式的阅读只会令人“虚胖”,缺乏实质的内涵。伯乐在线博客团队正试图以我们微薄的力量,把优秀的原创/译文分享给读者,做一个小而精的精选博客,为“快餐”添加一些“营养”元素。
新浪微博:
推荐微信号
(加好友请注明来意)
– 好的话题、有启发的回复、值得信赖的圈子
– 分享和发现有价值的内容与观点
– 为IT单身男女服务的征婚传播平台
– 优秀的工具资源导航
– 翻译传播优秀的外文文章
– 国内外的精选文章
– UI,网页,交互和用户体验
– 专注iOS技术分享
– 专注Android技术分享
– JavaScript, HTML5, CSS
– 专注Java技术分享
– 专注Python技术分享
& 2016 伯乐在线
赞助云主机

我要回帖

更多关于 linq equals 不等于 的文章

 

随机推荐