首页 > 图书中心 >图书详情
C#高级编程(第10版) C# 6 & .NET Core 1.0
作者:[美] Christian Nagel 著 李铭 译
丛书名:开源.NET生态软件开发
定价:168元
印次:1-2
ISBN:9787302461968
出版日期:2017.03.01
印刷日期:2017.07.14
提升C#和.NET技能的必备参考资源 Visual Studio 2015、ASP.NET Core 1.0和Universal Windows Platform的推出,为开发人员提供了使用C#创建应用程序的新方式。作为经典C#畅销书 ,本书涵盖了充分利用升级功能,积极简化工作流程所需的全部信息。本书循序渐进地讲解了Visual Studio 2015、.NET Core 1.0、ASP.NET MVC、Universal Windows Platform、WPF的最新变化。每一章都提供了清晰的解释、下载的代码,并从专家的角度提供了有价值的视野。有本书在手,读者可以快速获得最新的特性和功能。 主要内容 ◆ 通过.NET Core 和C# 6的增强功能,重新塑造了.NET平台,包括新增的.NET编译器平台 ◆ 展示了如何使用新的Visual Studio工具和用于ASP.NET Core 1.0、ASP.NET MVC、Web API、WPF和Universal Windows Platform的模板 ◆ 介绍允许应用程序在Windows、Linux和OS X上运行的.NET Core框架,还囊括了任务和并行编程,访问文件系统,以及网络堆栈 ◆ 演示了如何使用ASP.NET MVC 6和Web API创建Web应用程序,以及利用SignalR和WebHooks的推送通知 ◆ 介绍专业开发功能,例如单元测试,通过MVVM模式在WPF和Universal Windows Platform之间共享代码
more >对于开发人员,把C#语言和.NET描述为最重要的新技术一点都不夸张。.NET提供了一种环境。在这种环境中,可以开发在Windows上运行的几乎所有应用程序。在Windows上运行的是.NET Framework以前的版本,新版本.NET Core 1.0不仅在Windows上运行,还在Linux和Mac系统上运行。C#是专门用于.NET的编程语言。例如,使用C#可以编写Web页面、Windows Presentation Foundation(WPF)应用程序、REST Web服务、分布式应用程序的组件、数据库访问组件、传统的Windows桌面应用程序,以及可以联机/脱机运行的Universal Windows Platform (UWP)应用程序。本书介绍.NET Core 1.0和完整的.NET Framework,即.NET Framework 4.6。如果读者使用以前的版本编写代码,本书的一些章节就不适用。 在可能的情况下,本书的示例都使用了.NET Core 1.0。本书的代码在Windows系统上创建,但也可以在其他平台上运行。可能需要对示例进行较小的改变,才能使它们在Linux上运行。阅读第1章可以了解如何构建用于Linux平台的应用程序,什么程序不能在Linux上运行?WPF应用程序仍然需要完整的.NET Framework,仅在Windows上运行。UWP应用程序使用.NET Core,但还需要Windows运行库。这些应用程序也需要Windows。这些UI技术都包含在本书的第Ⅲ部分中。 那么,.NET和C#有什么优点? 0.1 .NET Core的重要性 为了理解.NET Core的重要性,就一定要考虑.NET Framework。.NET Framework 1.0在2002年发布,此后大约每两年就推出一个新的主要版本。Visual Studio 2013附带着C# 5和.NET 4.5。 .NET Framework 4.5十分巨大,有20 000多个类。 这个巨大的框架有什么问题?.NET Core是如何解决的? 对于新的开发人员来说,掌握这个巨大的框架并不容易。其中保留了旧应用程序很重要的许多内容,但它们对新的应用程序并不重要。对于有经验的开发人员来说,在这些技术中选择一个最好的是不容易的。必须为Web应用程序选择使用ASP.NET Web Forms还是ASP.NET MVC,为客户端应用程序选择使用Windows Forms和WPF还是Universal Windows Platform,为数据访问选择Entity Framework还是LINQ to SQL,为存储集合选择使用Array List还是List<T>。这对于一些有经验的开发人员而言,选择是显而易见的,但对于大多数开发人员来说,选择并不是那么容易。刚开始接触.NET的开发人员就更困难了。 .NET Core基于较小的单元—— 小型NuGet包。Console类只用于控制台应用程序。在 .NET Framework中,Console类可用于mscorlib,mscorlib是每个.NET应用程序都引用的程序集。使用.NET Core,必须显式地决定使用NuGet包System.Console;否则,Console类就不可用。 较小的包更容易摆脱框架的某些部分。如果需要给遗留应用程序使用旧的集合类,它们就可以通过NuGet包System.Collections.NonGeneric来使用。对于新的应用程序,可以定义能使用的软件包列表,System.Collections.NonGeneric可以排除在这个列表之外。 如今,开发要快得多。在许多产品中,客户会收到产品的持续更新,而不是每两年接收一次新版本。甚至Windows 10都具备这么快的步伐。客户在每次更新时都收到较小的新特性,但收到新特性的速度更快。.NET Framework目前的发布周期是两年,还不够快。一些技术,如Entity Framework,已经绕过了这个问题,它可以通过NuGet包提供新功能,而NuGet包可以独立于.NET Framework来发布。 更新较小的包,就允许更快的创新。.NET Core基于许多小型NuGet包,所以更容易改变。.NET Core和ASP.NET现在是开源的。.NET Core的源代码在http://www.github.com/dotnet上,ASP.NET的源代码在http://www.github.com/aspnet上。 发布.NET时,Windows在客户端和服务器上都有很大的市场份额。现在,世界更加碎片化。许多公司决定不通过ASP.NET运行服务器端代码,因为它不在Linux上运行。而ASP.NET Core 1.0和.NET Core可以在Linux上运行。 .NET Core独立于平台,支持Windows、Linux和Mac系统。对于客户端应用程序,可以在iPhone和Android上使用.NET和Xamarin。 .NET Framework要求把开发过程中使用的.NET运行库版本安装到目标系统上。基于客户需求,许多应用程序的开发都受到要使用的.NET Framework版本的限制。这不仅是基于客户端的应用程序开发的问题,也是基于服务器的应用程序开发的问题。我们不得不转回旧的.NET运行库版本,因为供应商不支持最新的版本。而有了.NET Core,运行库会和应用程序一起交付给客户。 建立ASP.NET时,与Active Server Pages(ASP,使用运行在服务器上的JavaScript或VBScript代码建立)的兼容是一个重要的方面。现在不再需要了。建立ASP.NET Web Forms 时,开发人员不需要知道任何关于JavaScript和HTML的内容,一切都可以用服务器端代码完成。现在,因为有了大量的JavaScript框架和HTML的增强,所以需要对JavaScript和HTML进行更多的控制。 在新版本的ASP.NET中,性能在框架体系结构中有很大的作用。只有真正需要的东西才施加性能影响。如果Web应用程序没有静态文件,就必须显式地决定是否使用它,否则就不会对它有性能影响。通过细粒度的控制,可以决定需要什么特性。 为了得到更大的性能提升,.NET Core可以构建为本地代码。这不仅在Windows上是可能的,在Linux和Mac系统上也是可行的。这样,在程序启动时可以得到特别的性能改进,而且使用更少的内存。 现在,遗留的应用程序有一个问题。大多数应用程序都不能轻松地切换到.NET Core上。完整的.NET Framework(仅运行在Windows上)也在进化。它进化的步伐没有.NET Core那么大,因为它是一个成熟的框架。在撰写本书时,发布了.NET 4.6.1,与之前的版本相比,其更新比较小。使用Windows Forms或ASP.NET Web Forms编写的应用程序仍然需要使用完整的框架,但它们可以利用.NET 4.6.1的增强功能。通过.NET 4.6.1,还可以使用为.NET Core建立的NuGet包。许多新的NuGet包采用便携的方式建立。通过ASP.NET MVC 5 Web应用程序,还可以决定改为运行在ASP.NET Core 1.0上的ASP.NET MVC 6。ASP.NET Core 1.0允许使用.NET Core或.NET 4.6。这可以简化转换过程。然而,要在Linux上运行ASP.NET MVC,则需要迁移ASP.NET MVC应用程序来使用.NET Core,但之前也不能在Linux上运行。 下面总结.NET Core的一些特性: ● .NET Core是开源的。 ● NuGet包较小,允许更快的创新。 ● .NET Core支持多个平台。 ● .NET Core可以编译为本地代码。 ● ASP.NET可以在Windows和Linux上运行。 从.NET Core的特性可以看出,自.NET第1版以来,这个技术在.NET历史上给.NET带来的变化最大。这是一个新的开始,我们可以用更快的步伐继续新的开发旅程。 0.2 C#的重要性 C#在2002年发布时,是一个用于.NET Framework的开发语言。C#的设计思想来自于C++、Java和Pascal。Anders Hejlsberg从Borland来到微软公司,带来了开发Delphi语言的经验。Hejlsberg在微软公司开发了Java的Microsoft版本J++,之后创建了C#。 C#一开始不仅作为一种面向对象的通用编程语言,而且是一种基于组件的编程语言,支持属性、事件、特性(注解)和构建程序集(包括元数据的二进制文件)。 随着时间的推移,C#增强了泛型、语言集成查询(Language Integrated Query,LINQ)、lambda表达式、动态特性和更简单的异步编程。C#编程语言并不简单,因为它提供了很多特性,但它的实际使用的功能不断进化着。因此,C#不仅仅是面向对象或基于组件的语言,它还包括函数式编程的理念,开发各种应用程序的通用语言会实际应用这些理念。 0.3 C# 6的新特性 在C# 6中有一个新的C#编译器。它完成源代码的清理。现在自定义程序也可以使用编译器管道的特性,并使用Visual Studio的许多特性。 这个新的C#编译器平台允许用许多新特性改进C#。虽然这些特性都没有LINQ或async关键字的影响大,但许多改进都提高了开发人员的效率。C# 6有哪些变化? 0.3.1 静态的using声明 静态的using声明允许调用静态方法时不使用类名。 C# 5 using System; // etc. Console.WriteLine("Hello, World!"); C# 6 using static System.Console; // etc. WriteLine("Hello, World"); using static关键字参见第2章。 0.3.2 表达式体方法 表达式体方法只包括一个可以用lambda语法编写的语句: C# 5 public bool IsSquare(Rectangle rect) { return rect.Height == rect.Width; } C# 6 public bool IsSquare(Rectangle rect) => rect.Height == rect.Width; 表达式体方法参见第3章。 0.3.3 表达式体属性 与表达式体方法类似,只有get存取器的单行属性可以用lambda语法编写: C# 5 public string FullName { get { return FirstName + " " + LastName; } } C# 6 public string FullName => FirstName + " " + LastName; 表达式体属性参见第3章。 0.3.4 自动实现的属性初始化器 自动实现的属性可以用属性初始化器来初始化: C# 5 public class Person { public Person() { Age = 24; } public int Age {get; set;} } C# 6 public class Person { public int Age {get; set;} = 42; } 自动实现的属性初始化器参见第3章。 0.3.5 只读的自动属性 为了实现只读属性,C# 5需要使用完整的属性语法;而在C# 6中,可以使用自动实现的属性: C# 5 private readonly int _bookId; public BookId { get { return _bookId; } } C# 6 public BookId {get;} 只读的自动属性参见第3章。 0.3.6 nameof 运算符 使用新的nameof运算符,可以访问字段名、属性名、方法名或类型名。这样,在重构时,就不会遗漏名称的改变: C# 5 public void Method(object o) { if (o == null) throw new ArgumentNullException("o"); C# 6 public void Method(object o) { if (o == null) throw new ArgumentNullException(nameof(o)); nameof 运算符参见第8章。 0.3.7 空值传播运算符 空值传播运算符简化了空值的检查: C# 5 int? age = p == null ? null : p.Age; C# 6 int? age = p?.Age; 新语法也有触发事件的优点: C# 5 var handler = Event; if (handler != null) { handler(source, e); } C# 6 handler?.Invoke(source, e); 空值传播运算符参见第8章。 0.3.8 字符串插值 字符串插值删除了对string.Format的调用,它不在字符串中使用编号的格式占位符,占位符可以包含表达式: C# 5 public override ToString() { return string.Format("{0}, {1}", Title, Publisher); } C# 6 public override ToString() => $"{Title} {Publisher}"; 与C# 5语法相比,C# 6示例的代码减少了许多,不仅因为它使用了字符串插值,还使用了表达式体方法。 字符串插值还可以使用字符串格式,把它分配给FormattableString时会获得特殊的功能。字符串插值参见第10章。 0.3.9 字典初始化器 字典现在可以用字典初始化器来初始化,类似于集合初始化器。 C# 5 var dict = new Dictionary<int, string>(); dict.Add(3, "three"); dict.Add(7, "seven"); C# 6 var dict = new Dictionary<int, string>() { [3] = "three", [7] = "seven" }; 字典初始化器参见第11章。 0.3.10 异常过滤器 异常过滤器允许在捕获异常之前过滤它们。 C# 5 try { //etc. } catch (MyException ex) { if (ex.ErrorCode != 405) throw; // etc. } C# 6 try { //etc. } catch (MyException ex) when (ex.ErrorCode == 405) { // etc. } 新语法的一大优势是,它不仅减少了代码的长度,而且没有改变堆栈跟踪——在C# 5中会改变堆栈跟踪。异常过滤器参见第14章。 0.3.11 Catch中的await await现在可以在catch子句中使用。C# 5需要一种变通方法: C# 5 bool hasError = false; string errorMessage = null; try { //etc. } catch (MyException ex) { hasError = true; errorMessage = ex.Message; } if (hasError) { await new MessageDialog().ShowAsync(errorMessage); } C# 6 try { //etc. } catch (MyException ex) { await new MessageDialog().ShowAsync(ex.Message); } 这个功能不需要增强的C#语法,现在就能工作。这个增强需要微软公司大量的投资才能实现,但是否使用这个平台真的没有关系。对用户来说,这意味着需要更少的代码——仅仅是比较两个版本。 0.4 UWP的新内容 Windows 8引入一种新的编程API:Windows运行库(Windows Runtime)。使用Windows运行库的应用程序可以通过Microsoft Store来使用,且有许多不同的名称。最初称它为Metro应用程序或Metro样式的应用程序,也称为Modern应用程序、Windows Store应用程序(尽管它们也可以通过PowerShell脚本来安装,不使用商店)和Universal应用程序。这里可能遗漏了一些名字。如今,这些都只是Windows应用程序,运行在UWP(Universal Windows Platform,通用Windows平台)上。 这些应用程序的理念是,让最终用户很容易通过Microsoft商店找到它们,提供便于触屏的环境,这个环境是现代的用户界面,看起来很干净、光滑,并允许流畅地交互,而且应用程序是可以信任的。更重要的是,已经了解Windows用户界面的用户应该被新的环境所吸引。 第1版的设计准则有诸多限制,有一些缺陷。如何在应用程序中寻找东西?许多用户在右边找不到工具栏,却发现它允许搜索许多应用程序。Windows 8.1把搜索功能搬到桌面的一个搜索框中。同时,如果用户不在屏幕上从上扫到下或从下扫到上,就经常找不到位于顶部或底部的应用栏。 Windows 10使设计更加开放。可以使用对应用程序有用的东西,在用户界面上做出决定,因为它与用户和应用程序最匹配。当然,它仍然最适合创建出好看的、光滑的、流畅的设计。最好让用户与应用程序愉快地交互,确定如何完成任务应该不是很难。 新的Windows运行库是Windows运行库3.0,它基于以前的版本,定义了XAML用户界面,实现了应用程序的生命周期,支持后台功能,在应用程序之间共享数据等。事实上,新版本的运行库在所有区域都提供了更多的功能。 Windows应用程序现在使用.NET Core。通过NuGet包与Windows应用程序可以使用相同的.NET库。最后,本地代码编译后,应用程序启动更快,消耗的内存更少。 与提供的附加功能相比,可能更重要的是现在可用的普遍性。Visual Studio 2013的第一次更新为Windows 8应用程序包括了一个新的项目类型:通用(Universal)应用程序。在这里,通用应用程序用3个项目实现:一个项目用于Windows应用程序,一个项目用于Windows Phone应用程序,另一个是共享的代码项目。甚至可以在这些平台之间共享XAML代码。新的通用项目模板包括一个项目。相同的二进制代码不仅可以用于Windows和Windows Phone,还可以用于Xbox、物联网(Internet of Things,IoT)设备和HoloLens等。当然,这些不同的平台所提供的功能不可能用于所有地方,但是使用这个不同的功能,仍然可以创建二进制图像,在每个Windows 10设备上运行。 0.5 编写和运行C#代码的环境 .NET Core运行在Windows、Linux和Mac操作系统上。使用Visual Studio Code(https://code. visualstudio.com),可以在任何操作系统上创建和构建程序。最好用的开发工具是Visual Studio 2015,也是本书使用的工具。可以使用Visual Studio Community 2015版(https://www. visualstudio.com),但本书介绍的一些功能只有Visual Studio的企业版提供。需要企业版时会提到。Visual Studio 2015需要Windows操作系统,要求使用Windows 8.1或更高版本。 要构建和运行本书中的WPF应用程序,需要Windows平台。Windows 7仍支持运行WPF应用程序。 要构建通用的Windows应用程序,可以使用Windows 8.1和Visual Studio,但要测试和运行这些应用程序,就需要Windows 10设备。 0.6 本书的内容 本书首先在第1章介绍.NET的整体体系结构,给出编写托管代码所需要的背景知识。此后概述不同的应用程序类型,学习如何用新的开发环境CLI编译程序。之后本书分几部分介绍C#语言及其在各个领域中的应用。 第I部分—— C#语言 该部分给出C#语言的良好背景知识。尽管这一部分假定读者是有经验的编程人员,但它没有假设读者拥有任何特定语言的知识。首先介绍 C#的基本语法和数据类型,再介绍C#的面向对象特性,之后介绍C#中的一些高级编程主题,如委托、lambda表达式、语言集成查询(LINQ)、反射和异步编程。 第II部分——.NET Core与Windows Runtime 该部分首先介绍全世界C#开发人员都使用的主要IDE:Visual Studio 2015。第17章学习Visual Studio企业版可用的工具。 第18章还要学习C#编译器的工作原理,以及如何使用.NET编译器平台以编程方式修改代码。 用C#代码创建功能时,不要跳过创建单元测试的步骤。一开始需要更多的时间,但随着时间的推移,添加功能和维护代码时,就会看到其优势。第19章介绍如何创建单元测试、网络测试和编码的UI测试。 第20~28章介绍独立于应用程序类型的.NET Core和Windows运行库。第20章学习如何从应用程序中写出也可以在生产环境中使用的诊断信息。第21章和第22章介绍了使用任务并行库(Task Parallel Library,TPL)进行并行编程,以及用于同步的各种对象。第23章学习如何访问文件系统,读取文件和目录,了解如何使用System.IO名称空间中的流和Windows运行库中的流来编写Windows应用程序。第24章利用流来了解安全性,以及如何加密数据,允许进行安全的转换。还将学习使用套接字和使用更高级别的抽象(如HttpClient,见第25章)的联网的核心基础。第26章讨论了Microsoft Composition,它允许创建容器和部件之间的独立性。第27章论述如何将对象序列化到XML和JSON中,以及用于读取和编写XML的不同技术。最后,第28章学会使用本地化的技术本地化应用程序,该技术对Windows和Web应用程序都非常重要。 第III部分——Windows应用程序 该部分的主题是使用XAML语法构建应用程序——UWP(通用Windows应用程序)和WPF。第29章介绍XAML的基础,包括XAML语法、依赖属性,以及标记扩展(可以创建自己的XAML语法)。第30章学习如何给基于XAML的应用程序指定样式。第31章主要关注MVVM(Model-View-View Model)模式,学习利用基于XAML的应用程序中的数据绑定特性,允许在很多UWP应用程序和WPF应用程序之间共享代码。使用Xamarin也可以为iPhone和Android平台分享很多开发代码。然而,本书不探讨如何使用Xamarin进行开发。论述UWP应用程序和WPF应用程序的介绍性章节后,有两章的内容讨论UWP应用程序的具体特征,另外两章讨论WPF应用程序。第 32章和第33章介绍具体的XAML控件与UWP应用程序,如RelativePanel和AdaptiveTrigger、新编译的绑定、应用程序生命周期、共享数据和创建后台任务。第34章和第35章论述WPF专用特性,如Ribbon控件,显示分层数据的TreeView、WPF专用的数据绑定功能,创建流和固定文档,创建XPS(XML Paper Specification)文件。 该部分的最后,第36章使用ClickOnce部署WPF应用程序,且包含在商店中获得UWP应用程序的信息。 第IV部分——Web应用程序和服务 该部分阐述Web应用程序和服务,还包含关于ADO.NET的两章内容。虽然也可以在客户应用程序中使用ADO.NET(第37章)和Entity Framework(第38章),但通常这些技术在服务器上使用,从客户端调用服务。 第39章学习如何创建自己的Windows服务,操作系统启动时,Windows服务就会运行。 ASP.NET的新版本ASP.NET Core 1.0参见第40章。其中讨论了ASP.NET的基础,以及如何使用这些基础知识建立ASP.NET MVC 6。ASP.NET MVC 6的特点参见第41章。 第42章讨论了ASP.NET MVC 6的REST服务特性:ASP.NET Web API。Web应用程序的发布和订阅技术使用ASP.NET技术WebHooks和SignalR的形式,在第43章中讨论。第44章讨论了使用SOAP和WCF与服务交流的旧技术。 与前一部分一样,该部分的最后讨论部署,包括部署运行在互联网信息服务器(Internet Information Server,IIS)上的网站,或使用Microsoft Azure托管网站。 0.7 如何下载本书的示例代码 在读者学习本书中的示例时,可以手工输入所有的代码,也可以使用本书附带的源代码文件。本书使用的所有源代码都可以从本书合作站点http://www.wrox.com/go/professionalcsharp6上下载。登录到站点http://www.wrox.com/上,使用Search工具或书名列表就可以找到本书。接着单击本书细目页面上的Download Code链接,就可以获得所有的源代码。也可以扫描封底的二维码获取本书的源代码。 在下载了代码后,只需要用自己喜欢的解压缩软件对它进行解压缩即可。另外,也可以进入http://www.wrox.com/dynamic/books/download.aspx上的Wrox代码下载主页,查看本书和其他Wrox图书的所有代码。 .NET Core更新很快,所以本书的源代码也可以在http://www.github.com/ProfessionalCSharp上获得。注意,GitHub上的源代码提供了实时的源文件,它们会与Visual Studio的次要更新版本一起更新,还有新的实验性C#特性。为了在本书出版后,更新源代码和额外的示例,可以查看GitHub网站。对应本书印刷版本的稳定的源代码可以从Wrox网站上获得。 0.8 勘误表 尽管我们已经尽力来保证不出现错误,但错误总是难免的,如果你在本书中找到了错误,如拼写错误或代码错误,请告诉我们,我们将非常感激。通过勘误表,可以让其他读者避免受挫,当然,这还有助于提供更高质量的信息。 要在网站上找到本书的勘误表,可以登录http://www.wrox.com,通过Search工具或书名列表查找本书,然后在本书的细目页面上,单击Book Errata链接。在这个页面上可以查看Wrox编辑已提交和粘贴的所有勘误项。完整的图书列表还包括每本书的勘误表,网址是www.wrox.com/misc-pages/ booklist.shtml。 如果在Book Errata页面上没有看到你找出的错误,请进入www.worx.com/contact/ techsupport.shtml,填写表单,发电子邮件,我们就会检查你的信息,如果是正确的,就在本书的勘误表中粘贴一条消息,我们将在本书的后续版本中采用。 0.9 p2p.wrox.com P2P邮件列表是为作者和读者之间的讨论而建立的。读者可以在p2p.wrox.com上加入P2P论坛。该论坛是一个基于Web的系统,用于发送与Wrox图书相关的信息和相关技术,与其他读者和技术用户交流。该论坛提供了订阅功能,当论坛上有新帖子时,会给你发送你选择的主题。Wrox作者、编辑和其他业界专家和读者都会在这个论坛上讨论。 在http://p2p.wrox.com上有许多不同的论坛,不只是可以帮助读者阅读本书,在读者开发自己的应用程序时,也可以从这个论坛中获益。要加入这个论坛,必须执行下面的步骤: (1) 进入p2p.wrox.com,单击Register链接。 (2) 阅读其内容,单击Agree按钮。 (3) 提供加入论坛所需要的信息及愿意提供的可选信息,单击Submit按钮。 (4) 然后就可以收到一封电子邮件,其中的信息描述了如何验证账户,完成加入过程。 加入论坛后,就可以发送新信息,回应其他用户的帖子。可以随时在Web上阅读信息。如果希望某个论坛给自己发送新信息,可以在论坛列表中单击该论坛对应的Subscribe to this Forum图标。 对于如何使用Wrox P2P的更多信息,可阅读P2P FAQ,了解论坛软件的工作原理,以及许多针对P2P和Wrox图书的常见问题解答。要阅读FAQ,可以单击任意P2P页面上的FAQ链接。
more >