尽管使用PHP和ASP.NET都可以构建复杂的Web应用程序(例如电子商务站点、Intranet和企业门户),但两者之间却存在若干重要差别。与PHP不同,ASP.NET并不是一种语言或分析器,而是Microsoft.NETFramework中的一组用于构建Web应用程序和XMLWebService的技术。同PHP一样,MicrosoftASP.NET页面在服务器上执行并生成标记(例如HTML、WML或XML),标记将发送给桌面应用程序或移动应用程序。但不同的是,ASP.NET为开发Web页面提供了强大的、面向对象的以及由事件驱动的编程模型,同时仍保留了PHP开发人员所习惯的那种简单性。
ASP.NET应用程序是基于强大的面向对象编程(OOP)模式,而不是基于脚本模式。这样便可以获得更为严谨的OOP功能,例如继承、封装和反射。虽然大多数基本的简单操作可以轻松地从PHP转换到ASP.NET,但要转换更为复杂的应用程序却并不这么简单,这需要仔细的规划和考虑,并采用更符合OOP的方法。
本文假设读者具有PHP方面的经验,并且对编程和软件开发有着基本了解。本文首先从代码方面对两者的基础体系结构的差别进行简单比较,并简要介绍一下OOP开发模型,然后比较两者的功能,最后将使用PHP和ASP.NET开发Web应用程序时的语法和常见任务作以对比。
注意:如果您希望跳过有关迁移的详细信息,而只想尝试ASP.NET的使用,则可以直接跳到体系结构对比
阅读完本文后,通过比较语法和语言,您将了解PHP和ASP.NET是很相似的,它们具有类似的功能和语法。但是,从更基本的体系结构层面来讲,PHP与ASP.NET却有着很大差别。PHP基于于平台的处理器/引擎,该处理器/引擎用于分析PHP脚本、提供数据库连接、符合Internet协议以及提供大多数Web应用程序平台所常见的众多其他任务。
ASP.NET是基于一系列技术(如CLR)构建的框架,提供了大量以很好的方式组织的类库,这些类库可满足要在Web应用程序中使用的大多数功能的需要。您还可以轻松地创建各种组件,对该框架进行扩展。
虽然PHP也提供了类似的功能(例如PEAR库),但PHP和ASP.NET并非完全相同,因为ASP.NET框架是完全基于OOP模式和OOP概念构建的,而PHP则不然。这种差别在PHP和ASP.NET中类和对象的访问方式方面表现得犹为明显。
虽然PHP和ASP.NET都为应用程序开发提供了OOP模式,但它们对各种OOP概念(例如封装和多态)的支持却存在差别。例如,PHP仅支持部分封装(例如,支持在类中声明方法和字段)和部分多态(没有重载和抽象)。此外,由于PHP的类中不存在私有(private)、公有(public)或受(protected)函数等概念以及重载,因此PHP也不支持这些概念及其访问。OOP纯粹主义者可能会说,ASP.NET以及各种语言也没有完全支持OOP模式中的所有概念,对于使用OOP的大多数语言(如C++和Java)来说,情况确实如此。
这既有积极的一面,也有消极的一面。消极的一面是:对于某些Web开发人员来说,学习ASP.NET要比学习PHP困难些,因为PHP提供的是开发人员创建Web站点时所的脚本模式。但是,使用过OOP语言和/或V的开发人员会发现ASP.NET比较亲切且易于学习。
ASP.NET支持OOP概念的积极的一面是,ASP.NET应用程序的大多数部分都会得到很好的设计,并且能够对内容、逻辑和数据进行清楚的分隔,因而从应用程序生命周期的长远角度看,这通常更容易为其提供支持。此外,由于ASP.NET本身支持企业技术,例如消息队列、事务(请.NETFramework的System.EnterpriseServices类)SNMP和Web服务,因而可以很容易地开发具有高度可缩放性的强大的应用程序。
请求PHP页面时,HTML和内联PHP脚本将编译为ZendOpcode(Zend操作代码)。Opcode是低级的二进制指令,是提供PHP页面时实际使用的代码。编译后,Zend引擎将运行Opcode(与Java的运行时引擎运行字节码类似),然后生成HTML并提供给客户端。
有许多商业产品可用来通过优化这些Opcode来加快PHP页面的执行速度。其他提高PHP脚本性能的方法还包括对Opcode和生成的HTML进行缓存。
当向IIS(Internet信息服务)或其他Web服务器请求.aspx页面(或ASP.NET支持的任何其他扩展名)时,该请求将被传递到ASP.NET进行处理。如果是第一次请求该页面,ASP.NET会将其编译为MSIL(Microsoft中间语言)。然后,CLR(公共语言运行库)会将此MSIL代码转换为机器码。最后使用这个编译后的代码运行该请求。后续的请求将使用相同的机器码运行(假设该页面未被修改)。
需要注意的是,因为由CLR生成的二进制代码已经最大限度地进行了优化,所以不必再使用其他产品来帮助提高性能。
此外还要注意,ASP.NET中的所有内容在运行前都被编译为机器码。甚至HTML文本也被转换为字符串常值控件,并按照适当的顺序插入到控件树中。
支持超过25种语言,但最常用的两种语言是VisualBasic.NET和C#。大多数开发人员都选择一种语言,但可以使用用任何其他支持的语言编写的组件。
直接支持OLE-DB和ODBC,本身还包含了MicrosoftSQLServer和Oracle的驱动程序。
可以调用各种软件包,并且开发人员可以直接修改Zend引擎(因为它是源代码的)。负责Zend的编程人员还可以使.NET对象能够从PHP中调用。
充分支持本身的C库和COM对象以及用任何与.NET兼容的语言(包括托管C++[ManagedC++])编写的程序集。对CORBA对象或Java类未提供内置支持。
为XMLDOM、XSLT、验证和XML文档的轻量级面向流的分析提供了广泛和易于使用的支持。
基于Cookie或无Cookie的会话状态(使用单个服务器内存存储、中央状态服务器或数据库后端)。此外,可扩展的体系结构还允许使用自定义的会话状态模块替换内置选项。要启用无Cookie的会话,只需更改配置。
PHP的内置功能涵盖了基于Web的应用程序可能需要执行的许多常见任务。通过某些额外的处理,它还可以访问Java类库。
支持Perl-5兼容的正则表达式,以及一些其他功能,例如从右向左匹配、预编译表达式、命名组及完全的Unicode支持。此外,还允许用户指定在正则表达式替换操作过程中调用的函数。
包含广泛的和信息,这些信息可包含在页面中或在单独的页面中显示。使用MicrosoftVisualStudio®.NET除了可以调试客户端脚本和SQLServer存储过程外,还可以轻松地在执行页面时对其进行交互式调试。
支持结构化的异常处理(其中新增了一个“finally”代码块,无论是否发生错误,都将执行该块),支持引发自定义异常,还支持为不同类型的不可恢复错误指定自定义错误页面。
完全支持。不但可以在单独的线程中同时请求多个页面,而且每个页面(如果需要)还可以生成自己的线程以执行同步或异步操作。
包含大量的缓存API,能够存储几乎任何类型的数据(包括数据库查询结果),并且能够基于时间、使用情况、文件或其他缓存项而过期。还允许在从缓存中删除给定项时调用用户定义的函数。
支持Unicode字符串和各种字符编码。日期、数字和货币函数均可区分文化,并根据当前文化的设置更改输出(而不要求调用其他函数)。支持使用资源文件对应用程序进行动态本地化。
SMTP支持依赖于IISSMTP服务。能够很好地支持HTTP。.NET不支持FTP和POP3,但是有一些免费的和销售的第三方组件可用来实现此功能。
最常用的工具是VisualStudio.NET,它为所有.NET语言、用于创建SQL以及测试数据库的数据库工具、Web设计工具、与版本控制的集成、高级调试功能以及许多其他功能提供了充分的支持。要获得完整的功能列表,请访问MSDN®
PHP允许您使用C、C++和Unixshell样式语法在代码中插入注释,注释器中的任何内容都不会被执行。
虽然PHP和VisualBasic.NET具有类似的语言构造,但语法却截然不同。由于VisualBasic.NET是基于OOP模型构建的,因而变量的声明要比PHP中变量的声明更严谨(在PHP中声明变量时只需在变量名之前添加一个$符号)。
在VisualBasic.NET中声明变量时,要指定它的变量名和特性。变量的声明语句是Dim关键字。它的和内容决定了变量的特性。变量具有级别(例如局部变量和模块变量)、数据类型、期以及可访问性。
虽然这种方法最初看上去要比在PHP中指定变量复杂,但实际上它会给开发人员带来方便。ASP.NET致力于帮助开发人员创建可靠的应用程序,而指定数据类型可以使诸如变量清理、调试、异常和错误处理以及代码等任务变得更加简单。
您可以在一个语句中声明多个相同类型的变量而不必重复数据类型。下面的语句将变量numStudents、numGTA和numProfessors声明为Integer类型:
变量的期是指变量可供使用的时间段。使用Dim语句声明的局部变量仅在其执行过程中存在。过程终止时,该过程的所有局部变量都将消失,这些变量的值也将丢失。
期这个概念很有用,因为它使开发人员在创建应用程序时不必考虑在大型应用程序中出现的许多问题,例如有效的内存管理。通过为变量选择正确的期,可以使.NET对未在使用的变量执行清理操作。
局部变量是在过程(过程与函数类似)中声明的变量。非局部变量是在过程之外但在类或结构中声明的变量。
在类或结构中,非局部变量的类别取决于该变量是否被共享。使用Shared关键字声明的变量是共享变量,它只有一个副本并且在类或结构的所有实例享。如果不使用Shared关键字,则该变量为实例变量,并且针对类或结构的每个实例都将创建一个单独的变量副本。实例变量的给定副本仅可用于为之创建该副本的实例。
但是,如果您在块中声明一个局部变量,则它的范围将只限于该块。局部变量在定义它的控制块中处于活动状态。控制块可以是过程、if语句以及循环语句等。
.NET支持变量的可访问性,从而使开发人员可以控制哪些代码能够访问特定的变量。例如,如果要为某个公式设置一些,并且要确保这些不会被它们所在类以外的其他代码更改,则可以使用private按如下所示声明该变量:
您可以使用上述任意关键字来声明模块、结构、类或实例变量。过程中只允许使用Dim关键字,并且可访问性始终为private。
然而,这些用于向浏览器发送输出的方法主要是为了保持与传统ASP的向后兼容性。ASP.NET提供了新的基于控件且面向事件的模型,您只需在服务器控件上设置属性即可将数据输出到浏览器中。通过这种技术可以将布局与代码清楚地分开,从而简化了工作,并且在复杂情况下所需的代码要比PHP少得多。
此示例声明了一个名为TheDate的服务器端标签(Label)控件,并在该页面的Load事件中将此标签的Text属性设置为当前的日期和时间。此代码的HTML输出与其他两个版本相同,不同的是Label控件将自身呈现为span标记(其中包含所设置的任何标签文本)。
切换语句是大多数编程语言的常见语言构造,用于通过多个值对单个表达式进行测试。它常用于替换包含多个elseif/else块的if语句。
在VisualBasic中,这种循环类型被称为For...Next循环,而在PHP中则称为For循环。在此示例中,我们使用了+=运算符作为sum=sum+counter的简写方式。在PHP中,可以使用Break;语句来中断循环。而For...Next循环可以通过ExitFor语句中断。
只要某个条件计算为true,条件循环便会持续重复一组指令。表8显示了这两种语言中的基本条件循环示例。
在VisualBasic中,这种循环类型称为Do...Loop语句,或while循环。PHP也支持Do...While循环,该循环与while循环非常类似,不同之处在于它是在每个循环的结尾而不是开始检查表达式。Do...While循环与常规while循环的主要差别是前者会确保运行第一个循环(因为只在该循环的结尾检查表达式),而常规的while循环则不一定运行。(因为它在每个循环的开始检查表达式,如果它一开始便计算为FALSE,该循环将立即结束执行。)
此循环只运行一次,因为在第一次循环后检查表达式时,表达式计算为FALSE($i不大于0),所以循环将停止执行。
但是,VisualBasic.NET支持内置的循环功能,这是PHP所不支持的。该功能将对条件进行计算,直到条件时为止。
PHP4(而非PHP3)包含一个foreach构造,它与ASP.NET以及某些其他语言中的构造非常类似。它只是为循环数组提供了一个简单的途径。foreach只适用于数组,如果试图将其用于其他数据类型的变量或未初始化的变量,则会导致错误。VisualBasic.NET中与此等价的循环是ForEach...Next语句。表9显示了在这两种语言中循环数组的示例。
PHP中的数组的功能与VisualBasic.NET中的数组的功能截然不同。PHP中的数组实际上是关联数组,但是可以像索引或关联数组那样使用。而VisualBasic.NET中的数组是索引数组。VisualBasic.NET不支持关联数组(但您可以自己创建它们,请下面的“集合”)。其他ASP.NET语言支持这些类型的数组,但VisualBasic.NET并不支持,这可能给PHP开发人员提出了一个难题,因为他们并不习惯使用这种更常见的索引数组,并且可能会希望在VisualBasic.NET中创建关联数组模型。表10显示了PHP和VisualBasic.NET中的简单数组示例。
与其他变量一样,VisualBasic.NET数组中的变量也是使用Dim语句声明的。变量名后面带有一对或多对括号,表明它是数组而不是标量(包含单个值的变量)。
创建VisualBasic.NET数组时,还必须将其声明为保存特定类型的数据。如果指定的类型是Object(作为.NET中所有对象类型的基础的普通类型),则此数组可以保存任何类型的数据,但是从数组中检索值时,必须将值转换为原来的类型。
VisualBasic.NET数组可以是嵌套的数组,也可以是数组。VisualBasic.NET中有许多与PHP相对应的用于处理数组的函数,但有一个例外。由于VisualBasic.NET不支持关联数组,因而不存在用于根据数组的“主键”来访问、索引或执行任何操作的函数。VisualBasic.NET中不存在这样的“主键”。
虽然本文多次提到VisualBasic.NET不支持关联数组,但是可以创建集合来代替数组。集合与关联数组有些相似,可以使用集合来解决某些类似的问题。
如果要处理一组小型的动态项目,则可以使用集合。要创建集合,只需如下面的示例代码所示声明并实例化一个Collection:
然后,您可以使用Add方法向集合中添加。在此示例中,我们创建了四个字符串并将它们添加到集合中。您还可以选择添加一个唯一的String值作为集合的主键。该值作为Add方法的第二个参数传递给集合。
虽然这看起来类似于在PHP中创建关联数组,但集合是一种截然不同的事物,因为它本身就是一个对象。对于转到ASP的PHP开发人员,我们在尝试在VisualBasic.NET中创建关联数组模型之前先了解一下Microsoft的
任何Web应用程序中都很常见的一个任务就是管理状态,该任务通常是使用Cookie或应用程序状态管理构造(如Session变量)来完成的。VisualBasic.NET具有与PHP类似的用于处理状态的方法。
在这两种中设置Cookie都是比较繁琐的。表11显示了在这两种语言中编写然后读取Cookie的示例。
ASP.NET中的Session变量与PHP中的Session变量非常类似。这两种中的Session变量都为确保Web应用程序访问中的一致性提供了相应处理和Cookie操作。
最重要的一个差别是,当从ASP.NET的Session对象中检索值时,该值将作为普通的System.Object类型返回,而这种类型可以保存任何类型的数据。要使用该值,必须先将其重新转换为原来的基本类型。表12显示了一些Session变量的使用示例。
ASP.NET还提供了另一种形式的状态管理,即“应用程序状态”,它与Session变量类似,但只是在应用程序的期中保持一致。这使您可以存储各种内容,例如配置信息或数据库连接字符串。这些内容在应用程序运行期间不会发生变化。
ASP.NET支持其他正则表达式实现(例如Perl和awk中的正则表达式)的大多数常见功能。这实际上是为了保持与Perl5正则表达式兼容。ASP.NET还支持其他正则表达式实现中未提供的正则表达式功能,例如从右向左匹配以及实时编译。由于ASP.NET与Perl正则表达式兼容,并且大多数PHP开发人员都使用了Perl兼容的正则表达式,因而通常无须将语法从一种形式转换为另一种形式。有关.NET正则表达式支持的详细信息,请
ASP.NET框架通过一种人们所熟悉的语言构造Try/Catch提供了结构化的异常处理,从而能够捕获代码中可能出现的异常。PHP没有提供此功能,但PHP5中将添加它。
要注意的是,Try块可以包含一个或多个Catch块,或包含一个Finally块,也可以同时包含两者。也就是说,如果您知道无法更正错误,但仍需要清除某些对象而不管是否发生错误,则可以使用不包含Catch语句的Try...Finally块。
在PHP中,通常可以通过两种常见方法访问数据库:使用数据库的特定扩展名或使用于数据库的PEARDB库。
在ASP.NET中,可以通过一组称为ADO.NET的对象来访问数据库,这些对象与PEARDB库的功能基本相同。数据库查询实际上是通过一组连接、命令、参数和数据适配器对象完成的。其中的每个对象都有多个版本,具体情况取决于所访问的数据库的类型。例如,有一组对象用于具有OLE-DB驱动程序的数据库(例如MicrosoftAccess),还有一组对象用于具有ODBC驱动程序而没有OLE-DB驱动程序的数据库。针对Oracle和MicrosoftSQLServer也有专门的数据提供者,它们已经过优化,可以分别为每种特定的数据库提供高性能的访问。某些第三方还提供了其他数据库支持,例如MySQL。本节中的示例将使用SQLServer对象,因为它是ASP.NET最常用的数据库。
图13显示了在这两种语言中执行查询的示例。在PHP中,我们显示了一个使用PEAR的连接,PEAR不仅是用于连接DB的最常用方法之一,但还是与ADO.NET最为类似的方法。
在PHP中,查询结果存储在一个称为resultset(结果集)的变量中,而在ADO.NET中,该变量称为Dataset对象。结果集是返回数据的只读视图,而.NETDataset实际上是返回数据在内存中的读写视图,这使.NET开发人员能够轻松地处理从数据源返回的数据。
输出数据时,ASP.NET提供了若干方法,用于向用户或客户端显示数据。第一种方法与PHP类似,即使用SQLDataReader对象遍历结果集以写出我们要通过查询显示的数据。更常用的方法是ASP.NET的数据绑定(PHP中没有与此对应的方法)。通过该方法,开发人员可以构建用户界面并显示可在应用程序中使用和重用的控件,还可以进一步根据数据和逻辑提取显示内容。数据绑定的语法很灵活,您不仅可以绑定到数据源,还可以绑定到简单的属性、集合、表达式,甚至是方法调用返回的结果。
要使用数据绑定,您需要向某个数据识别服务器控件(例如DataGrid)的DataSource属性指定某个数据源(例如查询结果)。您还可以选择为每个列提供一些附加的格式化信息并调用DataBind()方法。服务器控件将负责完成剩余的工作。
数据识别服务器控件将提供其他功能,例如支持对显示的数据进行分页或内联编辑。有关详细信息和示例,请
对经常访问的数据进行缓存可以显著缩短站点的响应时间,因为不需要等待数据库查询便可进行页面处理。而缓存由页面请成的HTML可以进一步缩短响应时间,因为根本就无需处理缓存的页面。这两种均支持上述两种缓存策略,但与PHP相比,ASP.NET提供了更多用于缓存和管理数据的方法,从而使开发人员可以针对自己应用程序的性能需要选择相应的方法和策略。
缓存某个页面请求的HTML输出是减少Web应用程序的负载的一种常用方法。PHP本身不支持页面缓存,但是可以通过编程的方式或下载第三方软件包来执行此操作。通常可以在服务器上通过多种方法执行页面缓存,这些方法可用于缓存编译的代码,直至将页面输出写出到一个单独的文件中(该文件将随代码的更新而更新)。
在ASP.NET中,可以通过低级别的OutputCacheAPI或高级别的@OutputCache指令来执行页面缓存。紧急迫降脱丝袜启用输出缓存后,将在对页面进行第一个GET请求时创建输出缓存条目。后续的GET或HEAD请求将由输出缓存条目支持,直到缓存的请求过期。
输出缓存将检查页面的过期策略和验证策略。如果页面位于输出缓存中并标记了过期策略(该页面自缓存时开始60分钟后过期),则60分钟后,该页面将从输出缓存中删除。如果在过期后收到另一个请求,将执行页面代码并再次缓存该页面。这种过期策略称为绝对过期,即页面在某个时间之前有效。
除了对整个页面进行输出缓存外,ASP.NET还提供了一个简单方法,使您可以只缓存页面的特定部分。这种方法称为拆分缓存。您可以使用@OutputCache指令将这些区域标记为缓存区域。该指令指定了用户控件的输出内容在服务器上缓存的时间(以秒为单位)以及影响这一时间的任何可选条件。
在PHP中,可以通过多种方法以编程方式将查询结果缓存到中,但PHP本身并未提供这种方法。在PHP中,可以创建数据缓存类或系统。对于少量信息,只需使用Session变量和/或Cookie;对于大量或复杂的信息,可以创建您自己的数据缓存类。但问题是,当处理包含很多类型的复杂数据时,这种方法的效率会很低且容易出错,在一定程度上会加大编程的难度。
对象提供了一个系统范围的数据缓存方法,可用于数据集(DataSet)、数组、集合、XML对象等。对于需要更复杂功能的应用程序,ASP.NET缓存还支持三种特定类型的缓存:过期、清理以及文件和关键项相关性。
过期类型的缓存使开发人员可以控制缓存项目的过期时间。可以将该时间定义为特定的时间(例如01:00),也可以将其定义为相对于上次使用项目时的时间,例如自上次访问该项目时开始20分钟后过期。过期后的项目将从缓存中删除,以后再试图检索该项目时,将返回空值(除非再次将其插入到缓存中)。
清理类型的缓存会在内存不足时尝试删除不经常使用或不重要的项目。开发人员可以控制清理的执行方式,并且可以在将项目插入到缓存中时向清理程序提供提示,以创建该项目的相对系统开销以及为使该项目有效而必须对其进行访问的相对频率。
文件和关键项相关性类型的缓存将基于某个外部文件或另一个缓存项目确定该缓存项目的有效性。如果相关项发生变化,缓存项目将失效并从缓存中删除。例如,假设您有一个较大的需要定期更新并下载给员工的报告,则可以在应用程序中使用此功能。应用程序处理文件和报告中的数据,然后缓存该数据并插入对该数据的来源文件(从该文件读取数据)的相关性。当该文件更新时,数据将从缓存中删除,应用程序可以重新读取它并重新插入更新的数据副本。
ASP.NET数据缓存为编程人员提供了许多不同的方法来管理应用程序并提高应用程序的响应速度和效率。有关详细信息,请Cache对象。
PHP和ASP.NET都为以编程方式发送电子邮件提供了内置的支持。要在此示例中使用ASP.NET发送电子邮件,您需要设置IISSMTP服务。因为.NET中的内置邮件对象依赖于该服务包含的对象,所以您必须安装它。尽管如此,与PHP一样,.NET也允许您使用任何SMTP服务器或邮件服务器。表14对这两种的基本语法进行了比较。
在PHP中,为分析和处理XML所提供的内置支持功能非常薄弱。虽然开发人员可以用它来分析和处理XML,但它不支持DOM分析(该分析尽管比PHP的SAX分析器速度慢,但更容易使用)。PHP也不支持根据DTD或XMLSCHEME对XML文档进行本地验证,它也不支持XSL/XSLT以及市场上许多Web应用程序常用的各种其他技术。虽然PHP可以通过很多PHP软件包完成许多与XML相关的任务,但.NET和ASP.NET为处理XML提供了广泛的内置支持。XML是.Net平台的核心技术之一。有关Web服务的详细信息,请
虽然可以在PHP中创建XML-RPC类型的机制,但创建Web服务(使开发人员能够使用常用协议和标准来交换数据和过程并将其用于查找、数据绑定和描述)。.NET为Web服务和相关技术(如SOAP、WSDL和UDDI)提供了广泛的支持。使用.NET,开发人员还可以轻松地创建和开发Web服务。例如,以下代码将创建一个简单的helloworldWeb服务:
利用.NETFrameworkSDK,您可以使用命令行Web服务描述语言工具(WSDL.exe)生成代理类。要为上述示例创建一个名为HelloWorld.cs的代理类,可以输入:
此类与前面创建的类非常相似。它包含一个名为SayHelloWorld的方法,用于返回字符串。将此代理类编译到应用程序中,然后调用此代理类的方法,使该代理类通过HTTP将SOAP请求打包并接收SOAP编码的响应,该响应随后被封装为字符串。
对于简单的小型应用程序,从PHP迁移到ASP.NET通常并不复杂。对于更复杂的应用程序,由于基础体系结构的不同以及ASP.NET的OOP模式,需要进行事先规划和精心设计,以利用ASP.NET提供的将显示与逻辑和数据明确分开的功能,以及可以节省时间的内置功能。与PHP相比,这些功能可以显著减少完成相同任务所需的代码数量。
本文由来源于325棋牌 325游戏中心唯一官方网站
网友评论 ()条 查看