C#语言和JAVA、C++的对比学习

很早以前,就听说著名的BorlandDelphi开发者,去微软设计了一门伟大的语言C#。但是由于一直都在Linux上做开发,所以无缘拜会。直到最近几年,借手游大潮,Unity3D引擎的流行,终于有机会真正使用一下这门著名的语言。在使用的过程中,不自觉的以前用过的语言Java和C++比较,发现了很多有趣的地方。

C#语言如果简单的来和Java以及C++对比,一句话的结论就是:C++的外表,JAVA的心。为什么这么说呢?原因是C#和Java都是带虚拟机的语言,所以拥有虚拟机的各种好处和缺点。比如它们都带有GC垃圾回收机制,都能跨平台。在手机领域,Android系统用Java来做SDK库语言是很有道理的,而跨手机平台的游戏引擎Unity3D,虽然同时支持JS和C#语言,但是借助MONO虚拟机,也可以实现不同操作系统的手机运行同一份代码。当然,虚拟机也有一些不如编译型语言的地方,比如不能简单的调用一些操作系统的库和系统API之类的问题。不过就语言特性本身来说,C#还是整合了很多C++的优点,可谓JAVA有的它有,C++有的它也有。下面我们就来看看C#有哪些好玩的特性:

  1. 首先我发现C#的命名规范比较特别,他使用的是Camel形式的单词拼写,但是又没有Java的首字母小写规则。这点其实更加像Google建议的C++编码规范。个人观点C#的命名规范是比较好看的,不过和我一样做linux下开发的同学往往更喜欢用下划线和小写字母拼接的写法。
  2. 类约束。C#可以用internal关键字限制类只能在一个名字空间内使用。这个特性可以帮助我们在开发SDK的时候,让内部实现的细节类不被外部代码触碰,从而维持良好的封装性。在以分层架构、微核架构下构建的复杂类库,这个特性还是很实用的。而C++则只能用名字空间来做编码上的提示,JAVA则更简单,只有“默认”权限控制符friendly标志,表示包内可访问。总体来说,JAVA也能做到C#的这种限制,但是没有那么直接。C++则完全是靠程序员的君子协定了,不过C++也有其他的招数,比如让头文件里面不包含那些不想被使用的定义。
  3. 数值类型方面,C#没有JAVA那么简单粗暴,它的整数是分有符号数和无符号数的,而且有byte类型和2个字节的char类型。这让C#在和C++通信的时候,对于整数的传递简单很多。而JAVA往往在和C++通信的时候,就要考虑那些负数是不是其实本来应该是正数。
  4. 静态方法的调用类名和小数点“.”连接,而不是C++的“::”连接。这种写法和JAVA是一样的,也更能表现出是属于一个“类”的特殊成员——静态方法。而C++的写法完全就是一种普遍的名字引用方式,虽然也无不妥,但是没有那么强的提示作用。
  5. C#的字符串对象,和JAVA类似,都是“不可变”的。由于有虚拟机的支持,所以字符串类型可以在内存中“无感知”的缓存。所以C#也提供了“可变”的字符串类型StringBuffer,需要字符串拼接的话,就直接用这个吧。不过说回来,C++的std::string和<<操作符好像也不是很受欢迎,还是有很多程序员喜欢用printf()这种字符串模板的方式来拼接字符串,可能性能比较高吧,不过一不小心就容易因为%d之类的标志符和变量类型对不上导致coredump。
  6. C#支持动态类型标识符var,这种变量的类型会在编译期推断,这个特性在C++升级版的C11里面才有对应的auto。而Java则是很笨的要写一堆类似String a = new String(“abc”);C++里面如果用到stl模板,特别是迭代器,也是一长串的std::map<int,char*>::integrator …。而dynamic这个关键字则更像是JAVA里面的Object,但是却没有JAVA里面烦人的类型转换警告。在类型标记上,C#无疑是花样最多的。
  7. 对于null这个东西,相信大家是又爱又恨。在java里面null可以是任何对象,在C++里面,null可能只是数字0的另外一个样子。而C#的null比较接近Java的含义,所以说C#实际上是“JAVA的心”。同时,它则提供了?和??操作符用来简化对null的写法。那些烦人的if( arg != null) arg = xxxx;的写法,在C#里面用??就能简单的解决了。这对于那些强制检查参数不能是null的项目来说,无疑能让代码更好看。
  8. 对于数据溢出,C#可以用checked/unchecked来标记代码段。Checked可以用来自动对数据溢出做判断而抛出异常,这在C++里面以前是要写一堆代码的,而且那些烦人的检查还可能要在各种数据运算那里都记得加上。
  9. 对于C++程序员来说,指针类型简直是最常用的东西。C#也能用指针,不过就要加上个unsafe关键字,说到底它认为直接用指针不安全。从这里看确实足够代表C++的外表了。
  10. C#在基本类型转换上,有Parse()/TryParse()/ToString()/ToInt()…等函数,和Java很像,但是用起来更简单。相比C++的类型转换就麻烦的多,还要考虑转换不成功的情况。
  11. C#的数组在用法上也更像Java,因为是带Length属性和溢出异常的。同时string也是一个数组。实际上这个设计和C++里面的std::string是类似的,只不过虚拟机的GC机制能简化内存的管理,也可以检查数组下标越界。
  12. C#的switch case可以识别字符串,这个特性直到java7之后才有,还是很实用的。C++则好像一直都不行,虽然不是什么很关键的特性,但是还是比较好用的。
  13. C#可以用预处理符号#if#define这些C语言的特性,在多平台编译上非常有用,相反JAVA就完全没有这个特性,只能依赖JNI去自己做适配,用起来还是不够方便。
  14. 关于变量的存储类型,C++是很丰富的,有值类型、引用、指针。而JAVA则很简单,基本类型都是值类型,对象基本都类似指针类型。C#再次取两者的并集,它可以既有值类型也有引用类型,比如struct关键字就专门用于值类型了。
  15. JavaBean规范虽然应用很广,但是语言一直没有提供支持,而是靠程序员的约定,这方面C#就走的更远,直接提供了{get;set}的自动合成,无需再写大堆的getter和setter代码。
  16. C#在关键字方面,更多的使用了C++的内容,比如有:constreadonly sealed base this,这些关键字在编译检查方面能帮程序员排除很多错误。JAVA则比较简陋一些,不过说回来很认真的使用const的程序员本身也会是很细心的程序员。
  17. C#在虚方法方面,采用了override和virtual机制,不过需要注意的是,对于interface来说,其覆盖方法只能用override而不能用virtual,而基类如果有virtual方法,其覆盖方法则必须用virtual,这对于从interface派生的两个层级的子类来说,是比较麻烦的,在这些方法上要用不同的关键字,这点看起来规定的很细致,似乎对于代码规范很好,但是有点过于繁琐了。个人还是比较喜欢C++的virutal约定,因为继承现在已经成为一个证明是“危险”的使用方法,JAVA可以随便继承覆盖的语法,现在看来已经不够稳健了。
  18. C#里面的关键字new和C++和JAVA都有点不同,是有一些特殊的用法的。除了用来构造对象,还可以用来隐藏基类被继承的成员,同时也可以用来在泛型使用中约束模板中的参数必须带有公共的无参数构造器——这点在JAVA里面也是靠所谓JavaBean规范来约定,而C#则直接提供了语言支持。
  19. Using关键字在c++里面是名字空间的使用符号。但是在C#里面,还可以用来构建自动资源回收的代码段:using(…){…} ,在代码段结束的时候,括号中创建的资源会被自动的调用IDisponse接口以回收资源。非内存资源回收这个老大难问题,在JAVA里面一直靠程序员去小心翼翼处理,而C#则把关键字和标准回收接口定义出来,让那些菜鸟程序员都能按着范例去做,不啻为一种很好的编程指导。
  20. C#的类型操作符非常丰富,有isas gettype() typeof()这些都和C++有相似的地方,也和JAVA有借鉴支出,总体来说是两门语言功能的并集。
  21. JAVA的构造函数继承一直是一个笑话,因为如果你在子类的构造函数中,调用了父类的某个特殊形态的构造函数,这个调用实际上不是在你的代码所在的地方运行的!这简直是对程序代码的欺骗。C++用构造函数列表的方式,来明确的表明构造过程,是比较清楚的。所以C#回到了C++这个优良的传统上,真不知JAVA的设计者为啥要把这个特性简化成现在这个可笑的样子。
  22. C#可以用代码来快速构造匿名对象new Object(…){x=…, y=…} List<int>{1,2,3,…} 这样的写法很有点lamda的风格,从开发效率来说还是很爽的。这种风格后来被C11和JAVA新版本争相使用,说明还是一个很好的设计。不过个人觉得不适合大规模使用,因为代码的可读性似乎有影响。
  23. Static本来是静态类而已,但是C#可以用staticclass来扩展一个已有类的方法,这真是太神奇了,这和Objective-C的扩展方法有的一拼。虽然从概念来说,这个特性似乎有破坏封装性的征兆,但是对于一些旧代码的维护来说,确实是非常方便的特征。
  24. 不定长参数三种语言都有,C#的params看起来比JAVA和C++的实现似乎都更优雅一点。
  25. C++没有专门的“接口类型”,而C#则和JAVA一样,有interface关键字。从OO的角度来说,“接口”这个定义还是很重要的,当然JAVA也许是为了多重继承来实现这个特性,但是C++程序中大家也尽量避免多重继承。所以接口类型往往更多带来了OO语意上的表达能力。
  26. 自动数值类型的拆箱和装箱 int I = (int) obj; object obj= I; 这种特性JAVA到后期的版本才有,也是被类型转换给逼的。说明C#这方面确实先进些。
  27. C#有yieldreturn; yield break;的用法,这在Unity3D中作为协程的基础非常实用。在C++里面我们往往会用一些看起来是函数调用的yield()来做goto的事情,实在是被迫无奈。
  28. C++的模板没有泛型约束的语法,这样导致写模板函数的时候有点像碰运气,靠编译检查来约束,而且还不能确定其真正的语意。但是JAVA和C#都有泛型约束语句,Where关键字还是很有必要的,特别是我们在做一些业务逻辑的时候,需要写泛型模板代码的时候。
  29. Delegate是C#中用的很多的一个特性,现在还和lamda表达式结合起来了。比c++的函数指针更方便,所以C11也增加了这方面的扩展。JAVA以前没有这个东西,动不动就要搞个接口再做继承,框架代码一大堆,虽然好理解些,但是写起来确实啰嗦。
  30. C#有专门的event,可以用+= ,-=来操作delegate,这在响应式编程、异步回调等地方使用非常方便。说回JAVA,这方面就非常笨拙,还是要依赖一堆interface。
  31. C#的标志库还提供了很多方便的工具模板,比如Tuple<T1, T2> Action<T1,T2…>, Func<Tresult, …> ,这种直接把一些最佳实践放在标准库的做法,我是非常欣赏的。虽然说没有这些东西程序一样能写,库也会更简洁,但是这些工具代码,能让整个C#生态有一些基础的代码标准。这些标准用法能大大降低代码的阅读理解的难度,可谓造福长久。比起很多团队订立各自的代码规范、实践标准来说,不如全世界都用一套。
  32. 最后说下注解,在Java里面叫Annotation,在C#里面叫Attribute,具体谁抄谁我不知道,但是自从有了这个特性,JAVA开源库里面各种配置文件迅速销声匿迹,代码和配置重新结合起来,对于程序员来说,绝对是代码维护的一种便利。C++由于是编译型的,语言层面是实现不了的了,从IDL之类的方面看可能还有希望。
  33. 最后C#支持LINQ,这个玩意太强大,属于独门暗器。

感谢大家的阅读,如觉得此文对你有那么一丁点的作用,麻烦动动手指转发或分享至朋友圈。如有不同意见,欢迎后台留言探讨。

原文发布于微信公众号 - 韩大(handa1740168)

原文发表时间:2016-01-30

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏北京马哥教育

Python 的正则表达式彩蛋

虽然我觉得在 Python 的标准库里的确有不少很恶心的库,但是 re 库肯定不属于这种。尽管它真的有年头没有更新了,但是在我看来,仍不失为动态语言中最好的库...

2627
来自专栏超然的博客

事件委托和this

  事件的第一个阶段是捕获阶段。事件从文档的根节点流向目标对象节点。途中经过各个层次的DOM节点,并在各节点上触发捕获事件,直到到达事件的目标节点。捕获阶段的主...

1113
来自专栏移动端开发

Swift 面向对象解析(二)

 接着上面一篇说的内容: 一 继承:      苹果继承与水果,苹果是水果的子类,则苹果是一种特殊的水果;这就是继承的关系,这个我们学OC的时候相信也都理解了...

2067
来自专栏tkokof 的技术,小趣及杂念

Sweet Snippet系列 之 元素删除

  平时代码总会遇到一些关于集合的操作,例如添加,排序等等,都可算作稀松平常,但是集合涉及的删除操作却一直有个大坑,我自己便跳进去过好几回,在此简单一记,以自警...

451
来自专栏阿杜的世界

《Java 8实战》阅读笔记(1)

给方法listFiles传递方法引用**File::isHidden**

794
来自专栏攻城狮的动态

iOS面试题梳理(一)

4016
来自专栏一个会写诗的程序员的博客

前端知识体系整理(不断更新)

var x = {}; var y = []; var z = null; typeof x; // "object" typeof y; // "objec...

732
来自专栏Coco的专栏

谈谈一些有趣的CSS题目(十)-- 结构性伪类选择器

1096
来自专栏圣杰的专栏

Ajax.BeginForm()知多少

在ASP.NET MVC中,Ajax.BeginForm扮演着异步提交的重要角色。其中就有五个重载方法,但是在实际应用中,你未必使用的得心应手,今天我们就从主要...

2016
来自专栏LeoXu的博客

Flex笔记_MX DataGrid、列表和树

columnCount、columnWidth、dataProvider、iconField、iconFunction、labelField、labelFun...

812

扫码关注云+社区