51

除了跨平台和平台成本以外,Java 还有什么特性是 C# 不具备的? - 知乎

 6 years ago
source link: https://www.zhihu.com/question/20363725/answer/261750022
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

除了跨平台和平台成本以外,Java 还有什么特性是 C# 不具备的?

425
244,305
登录后你可以
不限量看优质回答私信答主深度交流精彩内容一键收藏

(长文预警)

本人极少在知乎上回答程序设计和编程语言方面的问题,回答化学问题倒是多一点,今天看到这个问题,作为一名十几年前的老MCSD,以及MCT,外加也是SCJP,觉得可以粗略回答一下:

.NET Framework设计之初,微软根本就没准备让它完全跨平台,在没有使用.NET Framework这个产品名称之前,.NET Framework在微软内部有两种内部称呼。

一种叫做NGWS(Next Generation Windows Services,下一代Windows服务),从这个名称看来,.NET的设计目标之一,就是要取代传统的Win32 API,成为微软的下一代API,最终统一Win32 API、Win64 API和WinCE API(Windows Mobile、Windows Phone等都可以认为是WinCE的一个分支)。

另一种叫做COM+ 2.0,这个称呼在今天的.NET中仍然存在痕迹。.NET Framework的核心,即托管执行(早期国内曾译作受控执行,Managed Execution)环境,或者说也就是虚拟机,称为.NET CLR(公共语言运行时),但早期称为COM+ 2.0 Runtime或者COM+ Runtime,简称COR 2.0,用文本编辑器打开.NET Framework SDK或者新版Windows SDK中的CORHDR.H头文件,可以清楚地看到COM+ 2.0这一称呼的残留痕迹,甚至连托管代码EXE文件的文件头数据结构,仍然叫做IMAGE_COR20_HEADER类型:

typedef struct IMAGE_COR20_HEADER
{
    // Header versioning
    DWORD                   cb;              
    WORD                    MajorRuntimeVersion;
    WORD                    MinorRuntimeVersion;

    // Symbol table and startup information
    IMAGE_DATA_DIRECTORY    MetaData;        
    DWORD                   Flags;           

	// The main program if it is an EXE (not used if a DLL?)
    // If COMIMAGE_FLAGS_NATIVE_ENTRYPOINT is not set, EntryPointToken represents a managed entrypoint.
	// If COMIMAGE_FLAGS_NATIVE_ENTRYPOINT is set, EntryPointRVA represents an RVA to a native entrypoint
	// (depricated for DLLs, use modules constructors intead). 
    union {
        DWORD               EntryPointToken;
        DWORD               EntryPointRVA;
    };

    // This is the blob of managed resources. Fetched using code:AssemblyNative.GetResource and
    // code:PEFile.GetResource and accessible from managed code from
	// System.Assembly.GetManifestResourceStream.  The meta data has a table that maps names to offsets into
	// this blob, so logically the blob is a set of resources. 
    IMAGE_DATA_DIRECTORY    Resources;
	// IL assemblies can be signed with a public-private key to validate who created it.  The signature goes
	// here if this feature is used. 
    IMAGE_DATA_DIRECTORY    StrongNameSignature;

    IMAGE_DATA_DIRECTORY    CodeManagerTable;			// Depricated, not used 
	// Used for manged codee that has unmaanaged code inside it (or exports methods as unmanaged entry points)
    IMAGE_DATA_DIRECTORY    VTableFixups;
    IMAGE_DATA_DIRECTORY    ExportAddressTableJumps;

	// null for ordinary IL images.  NGEN images it points at a code:CORCOMPILE_HEADER structure
    IMAGE_DATA_DIRECTORY    ManagedNativeHeader;

} IMAGE_COR20_HEADER, *PIMAGE_COR20_HEADER;

#else // !__IMAGE_COR20_HEADER_DEFINED__

// <TODO>@TODO: This is required because we pull in the COM+ 2.0 PE header
// definition from WinNT.h, and these constants have not yet propogated to there.</TODO>

也就是说,微软在设计.NET Framework之初的两个主要目标,一个是统一下一代Windows系统的API;另一个就是取代繁杂的COM/COM+ 1.0组件技术,成为下一代组件技术COM+ 2.0。跨平台根本不是微软设计.NET Framework的主要目标,尽管从基本原理上讲,.NET CLR(或者COM+ 2.0 Runtime)和Java VM一样都是虚拟机,也有一套自己的虚拟机指令,即MSIL代码指令或者CIL代码指令,理论上是可以跨平台的。

微软虽然也开放了.NET CLI(公共语言基础结构)标准,即ECMA-335标准,在.NET CLI中公开了MSIL代码指令、元数据格式等设计虚拟机、编译器等所必需的信息,理论上可以设计出一个非Windows平台的.NET CLR甚至.NET Framework子集,例如Mono,但实际上这样设计出的.NET Framework根本不能完全取代微软官方提供的.NET Framework,仍然有相当部分的.NET应用程序不能执行,原因主要有两个:

1、大量的.NET相关服务(或者类库、API),实际上只是Win32(或者Windows)相关服务的一种包装,实现起来根本无法离开Windows操作系统,如果要独立于Windows操作系统重新实现这些API是相当复杂甚至不现实的,典型例子就是实现分布式事务等功能的企业组件服务(Enterprise Services),可以认为相当于Java J2EE中的EJB,它实际上是COM+企业组件服务(1.0或者1.5版本)的一种包装,而COM+企业组件服务是Windows相关服务的一部分,可以独立于.NET Framework使用,实际上与.NET Framework并无关系。

2、.NET CLR允许托管代码,也就是虚拟机(MSIL或者CIL)代码与本机代码混合执行。例如在使用Visual C++开发.NET应用程序时,编译时加上/clr开关则可以编译成托管代码,但如果在程序中使用#pragma unmanaged编译指示,则可以将一部分源程序编译为本机代码,而.NET CLR支持这两种代码共存于同一个EXE文件中,并在EXE文件执行时,在.NET CLR支持下混合执行,这种混合代码EXE文件显然是不能跨平台执行的。.NET CLR的这一特性,使之更接近一个能运行托管代码,但也支持本机代码运行的运行时环境(Runtime),而不是一个纯粹只运行虚拟机指令代码的虚拟机(VM),因此微软并不把.NET CLR或者COM+ Runtime像JVM一样称为“.NET VM”。

从以上两点也可以看出,微软设计.NET Framework的目的主要是向下一代Windows平滑过渡,跨平台并非首要目标,这一点微软显然还是做到了,从Windows Vista/7开始,即Windows NT 6.0/6.1开始,.NET Framework已经是Windows的一部分了。至于从Windows 8开始,Windows Runtime、Metro以及UWP的暂时失利,反而与.NET Framework关系不大,如果一开始Windows Runtime就完全构建在.NET Framework(甚至.NET Core)上,可能反而容易成功,微软坚持要将Windows Runtime构建在传统Win32和COM组件技术上,导致了Windows Runtime繁杂难于轻量化。

顺便说说,同样一种技术要同时适用于重量化和轻量化平台,往往会就高难就低,或者就低难就高,可能导致无法真正适用于其中一种平台(甚至在两种平台上都不适应),Windows 8/8.1/10和Windows Runtime目前看来就是一个不好的例子,微软以前也有过先例,试图用WPF统一桌面应用程序和浏览器RIA应用,结果并不成功,不得不为浏览器RIA应用专门推出WPF的轻量化版本,即WPF/E,也就是Silverlight。

既然Java和.NET两种平台的设计目标本来就不是完全相同的,那么比较Java编程语言和C#编程语言的特性,就不能脱离开相应平台进行比较。从两种语言的基本语法来看,C#与Java大约有60%左右的相似程度,但这一相似并不应该完全归结为C#抄袭Java,实际上,从编程语言的细节,类库的设计乃至于整个.NET Framework的设计,C#源自Delphi的痕迹反而更明显,这一原因是显然的——Delphi之父Anders Hejlsberg,也就是Visual J++(微软版本的Java,也就是今天的J#)之父,最后也就成了C#以及.NET Framework之父——同一个人,三者同源痕迹极其明显。

可视化程序设计所必需的事件处理为例,事件处理,通常需要在引发事件的控件中设置事件监听者(Event Listener)或者事件连接点(Event Connection Point),使之指向控件容器提供的事件处理者(Event Handler),而在事件监听者和事件处理者的设计上,Java和C#完全不同,而C#与Delphi很相似:

Java(例如Swing,甚至包括Android):事件监听者通过事件监听者接口引用实现,而事件处理者通常是匿名内部类对象。

C#和Delphi:事件监听者(事件连接点)是指向对象方法的指针,C#中称为Delegate,Delphi则是专门定义的一种指向方法的指针,而事件处理者则通常是控件容器类中实现的方法。

顺便说说,C++既不支持匿名内部类,又不支持指向不确定类方法的指针,因此C++实现事件处理有一定的困难。Java的匿名内部类重写方法,可以直接访问方法之外环境中的局部变量,具有闭包(Closure)特性;C#的Delegate可以直接指向不确定类对象的某一方法,只要方法参数和返回值相符即可通过编译,与对象的类可以无关,即Delegate可以同时指向对象以及对象的方法,我们称Delegate是一种闭包指针(Closure Pointer);而C++对闭包和闭包指针的支持都有限,仅Lambda表达式等功能有限支持闭包。为了克服C++在事件处理中的这个弱点,MFC实现了消息映射;Qt做了编译预处理,通过记录对象元数据,以及信号—槽等复杂机制(类似于Java或者C# Reflection的原理)实现事件处理;Delphi的“孪生兄弟”C++ Builder干脆给C++扩展了一个__closure关键字,让C++也支持闭包指针。

可以看出,C#在推出之初,可以认为结合了Java和Delphi的优点,还吸收了一部分C++的优点,因此C# 1.0一推出就显示出很多优于当时Java版本的语言特性,例如对运算符重载的支持,尽管是很有限的,但也使得String类和字符串的使用等,C#比Java简单直接得多;再例如对自动装箱拆箱的支持、Delegate的支持、非安全代码的支持等,C# 1.0就显得方便得多。.NET Framework 2.0开始在.NET CLR中加入了有限的泛型(参数化类型)支持,因此C# 2.0也能有限地支持泛型,这一点又走在了Java的前面。但Java也不是止步不前的,Java也反过来向C#学习,到了JDK 1.5,即Java 5,Java也引入了自动装箱拆箱、有限泛型支持等类似C#的新特性。C# 4.0以上,以及JDK 7以上,二者都引入了动态语言特性。可以说,目前C#和Java基本是不相上下,语法糖果可能C#更多更方便一些,但Java也有简练易学的优势。

但如果将比较目标从编程语言延伸到二者所在平台上,二者设计之初目标差异导致的结果就非常明显了,.NET Framework设计时就没有把跨平台作为主要目标,因此今天C#基本也只能用于Windows平台,虽然有Mono等开源平台,微软也有.NET Core等跨平台补救措施,但它们并不能完全取代完整版基于Windows的.NET Framework,相比Java在非Windows以及Windows平台上多年积累的开源和非开源生态环境,包括Android,.NET和C#肯定是迟了一步,前景需要观望。至少目前Windows Runtime、Metro和UWP不能说成功,在传统PC客户端Windows应用程序开发这一领域,为了保证Win7甚至WinXP的兼容性,谁也不敢轻易放弃Win32,微软早就想淘汰的Windows Forms,甚至已经有25年以上历史的MFC仍然在Win32开发一线挑大梁,这是事实。

在目前和未来一段时间内,Windows开发用C#,互联网和移动平台更多使用Java,这将是一个客观存在的事实,未来如何,只能拭目以待了。毕竟百足之虫,死而不僵,作为像微软这样有40年以上历史的老牌IT公司,又有相当的实力,是不会在互联网和移动时代,以及Java的冲击面前坐以待毙的,必然有其反制措施,老企业大企业思维僵化,内部利益集团牵扯多,决策颟顸,执行低效是全世界的通病,但体量和实力在那里摆着,不排除有中兴,或者说凤凰涅磐的可能性,不必过早下定论。编程语言终究是一通百通,兼容并蓄也不失为一种积极的态度。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK