C#中引用类型有哪些的问题

> 问题详情
在 C中由值类型转换为引用类型称为(),有引用类型转换为值类型称为()
悬赏:0&答案豆
提问人:匿名网友
发布时间:
在 C#中由值类型转换为引用类型称为(),有引用类型转换为值类型称为()请帮忙给出正确答案和分析,谢谢!
网友回答(共0条)
我有更好的答案
您可能感兴趣的试题
113.SQL SERVER 中索引类型包括的三种类型分别是() ()()。214.软件工程的三大文档()()()。
相关考试课程
请先输入下方的验证码查看最佳答案
图形验证:
验证码提交中……来自微软的Mads Togersen在近期所提出的一条提议,即在.NET社区中引起了热烈的争论。人们对此提议的反应大相径庭,既有人对此表示赞赏,也不乏倾向于保持现状的意见。在Reddit上,这条提议引起了大量关于向后兼容性方面的疑问。Strilanc认为,:这条提议还有待改进,它对于保证二进制兼容性、源代码兼容性以及现有代码的渐进式过渡方面还存在着一些考虑不周的情况。该提议造成了程序集级别上的意义转变,每个引用类型的名称意义都将变为不可空。它将一次性让整个项目级别的代码块的意义发生巨大的改变,要顺利地完成这一过程,需要付出大量的成本并承担极高的风险。这一点非常糟糕。该提议在泛型方面还有待改善,它完全没有提及在大量的泛型代码中将不允许使用default(T)这一事实。这一点对于现有的代码将产生怎样的影响?可以采取哪些解决手段?那些确实需要这一功能的类型又将如何实现default(T)的效果?这些问题都还没有进行充分的探索。这种方式岂不是会允许数组包含一些无效的初始值吗?这种做法公然地违反了类型系统的意义,既然如此,何必还要将它硬塞进去呢?还有一方面的顾虑在于,正如Maplemario所说:那么问题来了。假设我要使用一个旧的类库,其中的函数都返回类型T,无法它是否是可空的。现在,该提议产生了语言范式上的转变,它将T视为不可空的T类型,而我所调用的某个函数却有可能返回null(在编写这个类库时,这种做法是合法的)。如果这种场景在整个程序中是一个偶尔才需要进行测试的用例,那么在理想的情况下,项目文档将指出这一点,而我在阅读文档后就知道应当在调用时进行空检查。或者因为我记得这是一段陈旧的代码,因此我将始终进行空检查。而在实际情况下,由于“T即代表着不可空的T”,因此我无需再进行空检查。如此一来,这段程序就会在我对空指针进行取值时崩溃。人们也在热烈地讨论这一提议的替代方案。用户00Davo倾向于。我也乐于让纯粹的T类型总是代表不可空的引用,而只有T?才能够接受空值,但这种改变对于向后兼容性来说就是一场恶梦。如果能引入一个全新的、明确的不可空引用符号,那么向后兼容性就会坚挺许多。比如使用T!符号,如何?而在有些人看来,实现这一提议会造成的问题过多了。Number127建议:遗憾的是,目前来看,如果要以一种优雅的方法引入不可空引用类型,会造成过多的兼容性问题。我认为最有希望的替代方案是在维持目前的类型系统的情况下,通过静态分析技术以检查某个引用是否能够保证不为空。在GitHub的页面上,人们同样在讨论静态分析这一方案。Paulo Morgado对此进行了更进一步的阐述,他表示这条提议:如果我的理解没错,这条提议其实就是一种增强版的方法契约而已。编译器在这里不会做出什么担保,更不用说运行时了。编译器所做的无非是对于那些声明为可空的变量进行数据流的分析而已。在另一个话题中,Tomas Petricek指出:,例如F#:该提议能否详细地说明一下如何在CLR级别保存可空的标注信息?(我猜测这些标注应当并不具有运行时的意义,它们只会表现为某种.NET attribute,或某种其它类型的元数据?)我希望未来某个版本的F#编译器能够辨识并理解这些标注信息,并定义某种“严格”模式,可空的类型在这种模式中将自动地暴露为option&'T& (或者差不多意思的某种类型)。对于不可空引用类型的争论其实并不新鲜,在过去几年中,对这一问题已经进行了多次讨论。正如原微软的首席开发者Eric Lippert所说,。原文地址:q.com/cn/news/2015/10/CSharp-Nullable-Debate.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注公众号dotNET跨平台(opendotnet) 
 文章为作者独立观点,不代表微头条立场
的最新文章
opendotnet在这里你可以谈微软.NET,Mono的跨平台开发技术,也可以谈谈其他的跨平台技术。
在这里可以让你的.NET项目有新的思路,不局限于微软的技术栈,横跨Windows,Linux 主流平台热门文章最新文章opendotnet在这里你可以谈微软.NET,Mono的跨平台开发技术,也可以谈谈其他的跨平台技术。
在这里可以让你的.NET项目有新的思路,不局限于微软的技术栈,横跨Windows,Linux 主流平台C#中引用类型的常量只能赋值为null,那可以拿来干什么?_c#吧_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0成为超级会员,使用一键签到本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:86,989贴子:
C#中引用类型的常量只能赋值为null,那可以拿来干什么?收藏
在什么地方会用到,null不是什么都没有吗,麻烦举个例子,谢谢!
刘备:军师,此次伐魏你有何妙计?
比如清除标识,一个引用类型的变量,指向了一个对象,为了保证单例,当使用完毕后需要清除这一部分内存地址中的内容,所以可以赋值为null
你可以用static readonly
不论什么时候开始,重要的是开始之后就不要停止。不论什么时候结束,重要的是结束之后就不要悔恨。
有些事,我们总是弄不懂;有些人,我们总是猜不透;有些道,我们总是悟不尽;有些理,我们总是想不通;有些坎,我们总是跨不过;有些伤,我们总是治不好;有些天,我们总是睡不着;有些情,我们总是说不出;有些爱,我们总是得不到。对不起,那些回不去的曾经。
本人小白 理解为引用后不再想引用了就用null
如果觉得生活是一种刁难,一开始就输了。如果觉得刁难是一种雕刻,迟早都会赢的。
传参时,必须要填又没有值,可以填null
登录百度帐号推荐应用
为兴趣而生,贴吧更懂你。或C# 程序员最常犯的 10 个错误 - 技术翻译 - 开源中国社区
当前访客身份:游客 [
已有文章 2341 篇
当前位置:
C# 程序员最常犯的 10 个错误
英文原文:
0人收藏此文章,
推荐于 2年前 (共 30 段, 翻译完成于 05-09) ()
参与翻译(10人):
, , , , , , , , ,
C#是达成微软(CLR)的少数语言中的一种。达成CLR的语言可以受益于其带来的特性,如跨语言集成、异常处理、安全性增强、部件组合的简易模型以及调试和分析服务。作为现代的CLR语言,C#是应用最为广泛的,其应用场景针对Windows桌面、移动手机以及服务器环境等复杂、专业的开发项目。
C#是种面向对象的强类型语言。C#在编译和运行时都有的强类型检查,使在大多数典型的编程错误能够被尽早地发现,而且位置定位相当精准。相比于那些不拘泥类型,在违规操作很久后才报出可追踪到莫名其妙错误的语言,这可以为程序员节省很多时间。然而,许多程序员有意或无意地抛弃了这个检测的有点,这导致本文中讨论的一些问题。
&翻译的不错哦!
本文描述了10个 C# 程序员常犯的错误,或应该避免的陷阱。
尽管本文讨论的大多数错误是针对 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);&&&&&&&//&50
&&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);&&&&&//&Blue
如你所见,尽管Point和Pen对象的创建方式相同,但是当一个新的X的坐标值被分配到point2时, point1的值保持不变 。而当一个新的color值被分配到pen2,pen1也随之改变。因此,我们可以推断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#中,值得类型不能为空。根据定义,值的类型值,甚至初始化变量的值类型必须有一个值。这就是所谓的该类型的默认值。这通常会导致以下,意想不到的结果时,检查一个变量是否未初始化:
&&class&Program&{
&&&&&&static&Point&point1;&&&&&&static&Pen&pen1;&&&&&&static&void&Main(string[]&args)&{
&&&&&&&&&&Console.WriteLine(pen1&==&null);&&&&&&//&True
&&&&&&&&&&Console.WriteLine(point1&==&null);&&&&//&False&(huh?)
为什么不是【point 1】空?答案是,点是一个值类型,和默认值点(0,0)一样,没有空值。未能认识到这是一个非常简单和常见的错误,在C#中
很多(但是不是全部)值类型有一个【IsEmpty】属性,你可以看看它等于默认值:
Console.WriteLine(point1.IsEmpty);&&&&&&&&//&True
当你检查一个变量是否已经初始化,确保你知道值未初始化是变量的类型,将会在默认情况下,不为空值。
&翻译的不错哦!
常见错误&#3: 使用不恰当或未指定的方法比较字符串
在C#中有很多方法来比较字符串。
虽然有不少程序员使用==操作符来比较字符串,但是这种方法实际上是最不推荐使用的。主要原因是由于这种方法没有在代码中显示的指定使用哪种类型去比较字符串。
相反,在C#中判断字符串是否相等最好使用Equals方法:
&&public&bool&Equals(string&value);&&public&bool&Equals(string&value,&StringComparison&comparisonType);
第一个Equals方法(没有comparisonType这参数)和使用==操作符的结果是一样的,但好处是,它显式的指明了比较类型。它会按顺序逐字节的去比较字符串。在很多情况下,这正是你所期望的比较类型,尤其是当比较一些通过编程设置的字符串,像文件名,环境变量,属性等。在这些情况下,只要按顺序逐字节的比较就可以了。使用不带comparisonType参数的Equals方法进行比较的唯一一点不好的地方在于那些读你程序代码的人可能不知道你的比较类型是什么。
&翻译的不错哦!
使用带comparisonType的Equals方法去比较字符串,不仅会使你的代码更清晰,还会使你去考虑清楚要用哪种类型去比较字符串。这种方法非常值得你去使用,因为尽管在英语中,按顺序进行的比较和按语言区域进行的比较之间并没有太多的区别,但是在其他的一些语种可能会有很大的不同。如果你忽略了这种可能性,无疑是为你自己在未来的道路上挖了很多“坑”。举例来说:
&&string&s&=&"strasse";
&&//&outputs&False:
&&Console.WriteLine(s&==&"stra&e");
&&Console.WriteLine(s.Equals("stra&e"));
&&Console.WriteLine(s.Equals("stra&e",&StringComparison.Ordinal));
&&Console.WriteLine(s.Equals("Stra&e",&StringComparison.CurrentCulture));&&&&&&&&
&&Console.WriteLine(s.Equals("stra&e",&StringComparison.OrdinalIgnoreCase));
&&//&outputs&True:
&&Console.WriteLine(s.Equals("stra&e",&StringComparison.CurrentCulture));
&&Console.WriteLine(s.Equals("Stra&e",&StringComparison.CurrentCultureIgnoreCase));
&翻译的不错哦!
最安全的实践是总是为Equals方法提供一个comparisonType的参数。
下面是一些基本的指导原则:
当比较用户输入的字符串或者将字符串比较结果展示给用户时,使用本地化的比较(CurrentCulture&或者CurrentCultureIgnoreCase)。
当用于程序设计的比较字符串时,使用原始的比较(Ordinal&或者&OrdinalIgnoreCase)
InvariantCulture和InvariantCultureIgnoreCase一般并不使用,除非在受限的情境之下,因为原始的比较通常效率更高。如果与本地文化相关的比较是必不可少的,它应该被执行成基于当前的文化或者另一种特殊文化的比较。
此外,对Equals&方法来说,字符串也通常提供了Compare方法,可以提供字符串的相对顺序信息而不仅仅中测试是否相等。这个方法可以很好适用于&,&&=,&&和&=&运算符,对上述讨论同样适用。
&翻译的不错哦!
常见误区 #4: 使用迭代式 (而不是声明式)的语句去操作集合
在C# 3.0中,LINQ的引入改变了我们以往对集合对象的查询和修改操作。从这以后,你应该用LINQ去操作集合,而不是通过迭代的方式。
一些C#的程序员甚至都不知道LINQ的存在,好在不知道的人正在逐步减少。但是还有些人误以为LINQ只用在数据库查询中,因为LINQ的关键字和SQL语句实在是太像了。
虽然数据库的查询操作是LINQ的一个非常典型的应用,但是它同样可以应用于各种可枚举的集合对象。(如:任何实现了IEnumerable接口的对象)。举例来说,如果你有一个Account类型的数组,不要写成下面这样:
&&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语句可以轻易地替换掉你代码中一个迭代循环(或嵌套循环)里的几十条语句。更少的代码通常意味着产生Bug的机会也会更少地被引入。然而,记住,在性能方面可能要权衡一下。在性能很关键的场景,尤其是你的迭代代码能够对你的集合进行假设时,LINQ做不到,所以一定要在这两种方法之间比较一下性能。
&翻译的不错哦!
#5常见错误:在LINQ语句之中没有考虑底层对象
对于处理抽象操纵集合任务,LINQ无疑是庞大的。无论他们是在内存的对象,数据库表,或者XML文档。在如此一个完美世界之中,你不需要知道底层对象。然而在这儿的错误是假设我们生活在一个完美世界之中。事实上,相同的LINQ语句能返回不同的结果,当在精确的相同数据上执行时,如果该数据碰巧在一个不同的格式之中。
例如,请考虑下面的语句:
decimal&total=(from&accout&in&myaccouts
where&accout.status==‘active"
&&&&&&&&&&&&&&&&&&&select&accout&.Balance).sum();
想象一下,该对象之一的账号会发生什么。状态等于“有效的”(注意大写A)?
好吧,如果myaccout是Dbset的对象。(默认设置了不同区分大小写的配置),where表达式仍会匹配该元素。然而,如果myaccout是在内存阵列之中,那么它将不匹配,因此将产生不同的总的结果。
&翻译的不错哦!
等一会,在我们之前讨论过的字符串比较中, 我们看见 == 操作符扮演的角色就是简单的比较. 所以,为什么在这个条件下, == 表现出的是另外的一个形式呢 ?
答案是,当在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类中的static方法(叫做“extension method”)
&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#中的类型安全,你最好选择使用一个泛型接口,而不是使用非泛型的借口。泛型接口中的元素类型是你在在声明对象时指定的类型,而非泛型中的元素是object类型。当使用一个非泛型的接口时,C#的编译器不能对你的代码进行类型检查。同样,当你在操作原生类型的集合时,使用非泛型的接口会导致C#对这些类型进行频繁的和操作。和使用指定了合适类型的泛型集合相比,这会带来很明显的性能影响。
另一个常见的陷阱是自己去实现一个集合类型。这并不是说永远不要这样做,你可以通过使用或扩展.NET提供的一些被广泛使用的集合类型来节省大量的时间,而不是去重复造轮子。&特别是,C#的&和CLI提供了很多额外的集合类型,像持久化树形数据结构,基于堆的优先级队列,哈希索引的数组列表,链表等以及更多。
&翻译的不错哦!
常见错误#8:遗漏资源释放
CLR 托管环境扮演了垃圾回收器的角色,所以你不需要显式释放已创建对象所占用的内存。事实上,你也不能显式释放。C#中没有与C++&delete对应的运算符或者与C语言中free()函数对应的方法。但这并不意味着你可以忽略所有的使用过的对象。许多对象类型封装了许多其它类型的系统资源(例如,磁盘文件,数据连接,网络端口等等)。保持这些资源使用状态会急剧耗尽系统的资源,削弱性能并且最终导致程序出错。
尽管所有C#的类中都定义了析构方法,但是销毁对象(C#中也叫做终结器)可能存在的问题是你不确定它们时候会被调用。他们在未来一个不确定的时间被垃圾回收器调用(一个异步的线程,此举可能引发额外的并发)。试图避免这种由垃圾回收器中GC.Collect()方法所施加的强制限制并非一种好的编程实践,因为可能在垃圾回收线程试图回收适宜回收的对象时,在不可预知的时间内致使线程阻塞。
&翻译的不错哦!
这并意味着最好不要用终结器,显式释放资源并不会导致其中的任何一个后果。当你打开一个文件、网络端口或者数据连接时,当你不再使用这些资源时,你应该尽快的显式释放这些资源。
资源泄露几乎在所有的环境中都会引发关注。但是,C#提供了一种健壮的机制使资源的使用变得简单。如果合理利用,可以大增减少泄露出现的机率。NET framework定义了一个IDisposable接口,仅由一个Dispose()构成。任何实现IDisposable的接口的对象都会在对象生命周期结束调用Dispose()方法。调用结果明确而且决定性的释放占用的资源。
&翻译的不错哦!
如果在一个代码段中创建并释放一个对象,却忘记调用Dispose()方法,这是不可原谅的,因为C#提供了using语句以确保无论代码以什么样的方式退出,Dispose()方法都会被调用(不管是异常,return语句,或者简单的代码段结束)。这个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#中进行显示类型转换的两种不同的方式:
&&//&方法&1:
&&//&如果&account&不能转换成&SavingAccount&会抛出异常
&&SavingsAccount&savingsAccount&=&(SavingsAccount)
&&//&方法&2:
&&//&如果不能转换,则不会抛出异常,相反,它会返回&null
&&SavingsAccount&savingsAccount&=&account&as&SavingsA
很明显,如果不对方法2返回的结果进行判断的话,最终很可能会产生一个 NullReferenceException 的异常,这可能会出现在稍晚些的时候,这使得问题更难追踪。对比来说,方法1会立即抛出一个&InvalidCastExceptionmaking,这样,问题的根源就很明显了。
&翻译的不错哦!
此外,即使你知道要对方法2的返回值进行判断,如果你发现值为空,接下来你会怎么做?在这个方法中报告错误合适吗?如果类型转换失败了你还有其他的方法去尝试吗?如果没有的话,那么抛出这个异常是唯一正确的选择,并且异常的抛出点离其发生点越近越好。
下面的例子演示了其他一组常见的方法,一种会抛出异常,而另一种则不会:
&&int.Parse();&&&&&//&如果参数无法解析会抛出异常
&&int.TryParse();&&//&返回bool值表示解析是否成功
&&IEnumerable.First();&&&&&&&&&&&//&如果序列为空,则抛出异常
&&IEnumerable.FirstOrDefault();&&//&如果序列为空则返回&null&或默认值
有些程序员认为“异常有害”,所以他们自然而然的认为不抛出异常的程序显得更加“高大上”。虽然在某些情况下,这种观点是正确的,但是这种观点并不适用于所有的情况。
&翻译的不错哦!
举个具体的例子,某些情况下当异常产生时,你有另一个可选的措施(如,默认值),那么,选用不抛出异常的方法是一个比较好的选择。在这种情况下,你最好像下面这样写:
&&if&(int.TryParse(myString,&out&myInt))&{&&&&//&use&myInt
&&}&else&{&&&&//&use&default&value
而不是这样:
&&&&myInt&=&int.Parse(myString);&&&&//&use&myInt
&&}&catch&(FormatException)&{&&&&//&use&default&value
但是,这并不说明 TryParse 方法更好。某些情况下适合,某些情况下则不适合。这就是为什么有两种方法供我们选择了。根据你的具体情况选择合适的方法,并记住,作为一个开发者,异常是完全可以成为你的朋友的。
&翻译的不错哦!
常见错误 #10: 累积编译器警告而不处理
这个错误并不是C#所特有的,但是在C#中这种情况却比较多,尤其是从C#编译器弃用了严格的类型检查之后。
警告的出现是有原因的。所有C#的编译错误都表明你的代码有缺陷,同样,一些警告也是这样。这两者之间的区别在于,对于警告来说,编译器可以按照你代码的指示工作,但是,编译器发现你的代码有一点小问题,很有可能会使你的代码不能按照你的预期运行。
一个常见的例子是,你修改了你的代码,并移除了对某些变量的使用,但是,你忘了移除该变量的声明。程序可以很好的运行,但是编译器会提示有未使用的变量。程序可以很好的运行使得一些程序员不去修复警告。更有甚者,有些程序员很好的利用了Visual Studio中“错误列表”窗口的隐藏警告的功能,很容易的就把警告过滤了,以便专注于错误。不用多长时间,就会积累一堆警告,这些警告都被“惬意”的忽略了(更糟的是,隐藏掉了)。
&翻译的不错哦!
但是,如果你忽略掉这一类的警告,类似于下面这个例子迟早会出现在你的代码中。
&&class&Account&{
&&&&&&int&myId;&&&&&&int&Id;&&&//&编译器已经警告过了,但是你不听
&&&&&&//&Constructor
&&&&&&Account(int&id)&{&&&&&&&&&&this.myId&=&Id;&&&&&//&OOPS!
再加上使用了编辑器的智能感知的功能,这种错误就很有可能发生。
现在,你的代码中有了一个严重的错误(但是编译器只是输出了一个警告,其原因已经解释过),这会浪费你大量的时间去查找这错误,具体情况由你的程序复杂程度决定。如果你一开始就注意到了这个警告,你只需要5秒钟就可以修改掉,从而避免这个问题。
记住,如果你仔细看的话,你会发现,C#编译器给了你很多关于你程序健壮性的有用的信息。不要忽略警告。你只需花几秒钟的时间就可以修复它们,当出现的时候就去修复它,这可以为你节省很多时间。试着为自己培养一种“洁癖”,让Visual Studio 的“错误窗口”一直显示“0错误, 0警告”,一旦出现警告就感觉不舒服,然后即刻把警告修复掉。
&翻译的不错哦!
当然了,任何规则都有例外。所以,有些时候,虽然你的代码在编译器看来是有点问题的,但是这正是你想要的。在这种很少见的情况下,你最好使用&#pragma warning disable [warning id] 把引发警告的代码包裹起来,而且只包裹警告ID对应的代码。这会且只会压制对应的警告,所以当有新的警告产生的时候,你还是会知道的。.
C#是一门强大的并且很灵活的语言,它有很多机制和语言规范来显著的提高你的生产力。和其他语言一样,如果对它能力的了解有限,这很可能会给你带来阻碍,而不是好处。正如一句谚语所说的那样“knowing enough to be dangerous”(译者注:意思是自以为已经了解足够了,可以做某事了,但其实不是)。
熟悉C#的一些关键的细微之处,像本文中所提到的那些(但不限于这些),可以帮助我们更好的去使用语言,从而避免一些常见的陷阱。
&翻译的不错哦!

我要回帖

更多关于 引用类型有哪些 的文章

 

随机推荐