提交 9fc3c76e 编写于 作者: W wizardforcel

2022-01-08 18:14:44

上级 f73ef918
......@@ -71,19 +71,19 @@
我们需要在 Microsoft Azure 上创建一个 DocumentDB 实例,您需要使用 Microsoft 帐户登录或注册 Azure。你可以通过访问 azure.microsoft.com 来实现。
![](../images/00003.jpeg)
![](img/00003.jpeg)
图 1:微软 Azure 登录屏幕
注册或登录 Azure 门户后,您可以浏览 Azure 服务列表并选择`DocumentDB Accounts`选项。
![](../images/00004.jpeg)
![](img/00004.jpeg)
图 Azure 服务列表中的文档数据库
选择文档数据库后,必须通过点击`Add`创建一个新的文档数据库账户。
![](../images/00005.jpeg)
![](img/00005.jpeg)
图 3:添加文档数据库帐户的屏幕
......@@ -91,13 +91,13 @@
该标识是微软 Azure 中文档数据库帐户的唯一全局标识符。要完成帐户的创建,请单击`Create`。DocumentDB 帐户创建过程可能需要几分钟时间。
![](../images/00006.jpeg)
![](img/00006.jpeg)
图 4:最终文档数据库帐户创建屏幕
图 5 描述了 DocumentDB 帐户在创建后将如何显示。
![](../images/00007.jpeg)
![](img/00007.jpeg)
图 5:文档数据库帐户仪表板
......@@ -105,13 +105,13 @@
唯一的要求是为新的文档数据库提供一个标识。
![](../images/00008.jpeg)
![](img/00008.jpeg)
图 6:新的文档数据库创建屏幕
创建文档数据库后,可以通过直观的仪表板进行配置。
![](../images/00009.jpeg)
![](img/00009.jpeg)
图 7:文档数据库仪表板
......@@ -119,13 +119,13 @@
图 8 描述了文档数据库的结构。
![](../images/00010.jpeg)
![](img/00010.jpeg)
图 8:文档数据库内部结构
收款是一个可计费的实体,其成本由与收款相关的绩效水平决定。性能级别(S1、S2 和 S3)提供 10GB 的存储和固定的吞吐量。
![](../images/00011.jpeg)
![](img/00011.jpeg)
图 9:文档数据库集合的性能级别和定价层
......@@ -135,7 +135,7 @@
点击仪表盘上的`Add Collection`可以创建一个集合。您需要指定定价级别(通过选择 S1)和索引策略。索引策略的默认设置实际上是默认设置。如果您想利用全字符串查询,只需在创建集合后在`Scale & Settings`选项中将索引策略从默认更改为哈希即可。打开`Indexing Policy`选项,将`indexingMode`属性从`consistent`更改为`hash`。对于简单可怕的客户关系管理,我们将使用哈希索引策略。
| ![](../images/00012.jpeg) | ![](../images/00013.jpeg) |
| ![](img/00012.jpeg) | ![](img/00013.jpeg) |
图 10:创建文档数据库集合并更改索引策略
......@@ -143,7 +143,7 @@
首先,启动 Visual Studio 2015 并创建一个 C#控制台应用程序。模板代码加载后,转到`Tools` > `NuGet Package Manager` > `Manage NuGet Packages`,在`Package Source`下拉控件中选择`nuget.org`。在搜索框中,输入`DocumentDB`,将显示软件包,可供安装。
![](../images/00014.jpeg)
![](img/00014.jpeg)
一旦。NET DocumentDB 客户端已经安装,您将在您的 Visual Studio 解决方案中引用`Microsoft.Azure.Documents.Client``Newtonsoft.Json`程序集。
......@@ -1868,11 +1868,11 @@ DocumentDB 支持数值字段的基于范围的索引,允许您进行范围查
`OpenFindCase`将调用包装在`CrmExample.OpenCase`周围, `which`创建一个`Incident`实例并调用该实例的`Open`方法。这会返回一个`Document`实例,然后反序列化为一个`IncidentInfo`对象。最后,这个`IncidentInfo`对象通过`OutputCaseDetails`打印在屏幕上。运行这个例子将产生如图 12 所示的结果。
![](../images/00015.jpeg)
![](img/00015.jpeg)
该文档可以在 Azure 上看到,如图 13 所示。
![](../images/00016.jpeg)
![](img/00016.jpeg)
图 Azure 文档浏览器中的事件
......@@ -2208,11 +2208,11 @@ DocumentDB 支持数值字段的基于范围的索引,允许您进行范围查
图 14 和 15 显示了屏幕上和 Azure 中的`FindByDescription`结果。
![](../images/00017.jpeg)
![](img/00017.jpeg)
图 14:在屏幕上查找描述结果
![](../images/00018.jpeg)
![](img/00018.jpeg)
图 15:Azure 中的 FindByDescription 结果
......@@ -2275,11 +2275,11 @@ DocumentDB 支持数值字段的基于范围的索引,允许您进行范围查
该操作产生图 16 和 17 中的结果。
![](../images/00019.jpeg)
![](img/00019.jpeg)
图 16:在屏幕上显示结果后查找
![](../images/00020.jpeg)
![](img/00020.jpeg)
图 17:在 Azure 中找到结果后的查找时间
......@@ -2339,7 +2339,7 @@ DocumentDB 支持数值字段的基于范围的索引,允许您进行范围查
图 18 显示了结果。
![](../images/00021.jpeg)
![](img/00021.jpeg)
图 18:屏幕上的查找结果
......@@ -2507,7 +2507,7 @@ DocumentDB 支持数值字段的基于范围的索引,允许您进行范围查
结果如图 19 所示。
![](../images/00022.jpeg)
![](img/00022.jpeg)
图 19:在 Azure 中添加注释结果
......
......@@ -30,13 +30,13 @@ CIL 代码是程序集的实际可运行部分。而不是将高级的、人类
ILSpy 不需要安装。它是一个压缩的档案,包含一个可执行文件和一组未压缩的动态链接库,这意味着它可以用来检查任何。NET 程序集。
![](../images/00023.jpeg)
![](img/00023.jpeg)
图 20:下载并解压后的 ILSpy
借助 ILSpy,让我们快速检查一下我们之前在 Simple Awesome CRM 项目中使用的`Newtonsoft.Json`程序集的元数据。
![](../images/00024.jpeg)
![](img/00024.jpeg)
图 21:使用 ILSpy 看到的程序集元数据属性
......@@ -147,7 +147,7 @@ ILSpy 不需要安装。它是一个压缩的档案,包含一个可执行文
这产生了如图 22 所示的输出。
![](../images/00025.jpeg)
![](img/00025.jpeg)
图 22:有和没有反射的速度比较的输出
......@@ -315,7 +315,7 @@ ILSpy 不需要安装。它是一个压缩的档案,包含一个可执行文
这个例子的代码执行产生了如图 23 所示的输出。
![](../images/00026.jpeg)
![](img/00026.jpeg)
图 23:供应商库的输出
......@@ -406,7 +406,7 @@ ILSpy 不需要安装。它是一个压缩的档案,包含一个可执行文
请注意,两种方法`VendorPrivateItems`(使用反射)和`VendorPublicItems`(不使用反射)产生完全相同的结果。
![](../images/00027.jpeg)
![](img/00027.jpeg)
图 24:调整后供应商库的输出
......@@ -616,7 +616,7 @@ ILSpy 不需要安装。它是一个压缩的档案,包含一个可执行文
获取程序集限定类型名的一个简单方法是在 ILSpy 中打开 TrendsPlugin.dll。这在 ILSpy 屏幕的绿色顶部清晰可见。
![](../images/00028.jpeg)
![](img/00028.jpeg)
图 25:使用 ILSpy 获取程序集限定的类型名
......
此差异已折叠。
......@@ -46,13 +46,13 @@
这个阶段将所有信息放在一起并显示图像。
| ![](../Images/note.png) | 注意:由于这只是对 Direct3D 的简短介绍,我们将使用的着色器只有顶点着色器和像素着色器。在后续的书中, [*Direct3D 简洁地*](https://www.syncfusion.com/resources/techportal/details/ebooks/direct3d) ,我们将更详细地考察 API。 |
| ![](img/note.png) | 注意:由于这只是对 Direct3D 的简短介绍,我们将使用的着色器只有顶点着色器和像素着色器。在后续的书中, [*Direct3D 简洁地*](https://www.syncfusion.com/resources/techportal/details/ebooks/direct3d) ,我们将更详细地考察 API。 |
## 第 16 章:启动 Direct3D 项目
要开始一个新的 Direct3D 应用,在 Visual Studio 主菜单中点击**文件** > **新项目**。您将看到“新项目”屏幕。点击左侧面板的 **Visual C++** ,然后点击中间面板的 **Direct3D App** 。给你的新项目起个名字。我的叫做 Direct3DTesting,如图 41 所示。
![](../Images/image051.jpg)
![](img/image051.jpg)
图 41:创建 Direct3D 应用程序
......@@ -64,7 +64,7 @@
我们将使用标准的笛卡尔 x,y 和 z 系统来描述我们的三维例子中的点。每个 x、y 和 z 值都指三维空间中的一个点。每个轴都可以看作是一个垂直于另外两个轴的无限平面。它们通常被总结为三行,如图 42 所示。
![](../Images/image052.png)
![](img/image052.png)
图 42:三维轴
......@@ -74,11 +74,11 @@
顶点是三维空间中的一个点,用于表示对象、形状的角或线的端点。为了指定一个三维顶点,我们需要提供前面提到的三个坐标。坐标几乎总是 32 位浮点值。每个元素(x、y 或 z)指定了沿维度的位置,它们共同描述了三维空间中一个精确且唯一的点。
| ![](../Images/note.png) | 注意:我将使用 x,y,然后 z 作为描述顶点时元素的顺序。所以类似(9.0f,8.0f,5.0f)的意思是 x 轴 9,y 轴 8,z 轴 5。 |
| ![](img/note.png) | 注意:我将使用 x,y,然后 z 作为描述顶点时元素的顺序。所以类似(9.0f,8.0f,5.0f)的意思是 x 轴 9,y 轴 8,z 轴 5。 |
在图 43 中,黄色的球代表一个顶点。现实中,顶点没有物理形态;它代表一个无限小的点。顶点在位置(1.2f,0.4f,0.7f)。这意味着它位于蓝色 x 轴原点右侧 1.2 个单位,绿色 y 轴原点上方 0.4 个单位,距离红色 z 轴原点 0.7 个单位。
![](../Images/image053.jpg)
![](img/image053.jpg)
图 43:一个点
......@@ -139,7 +139,7 @@
现在,您应该能够运行应用程序并查看美丽的彩虹色三角形,如图 44 所示。
![](../Images/image054.jpg)
![](img/image054.jpg)
图 44:三角形
......@@ -173,7 +173,7 @@
为三角形定义顶点的顺序决定了三角形的哪条边是前面,哪条边是后面。索引缓冲区通过指定顶点缓冲区中用于构建我们的形状的点的顺序来描述这个顺序(这与我们在 Direct2D 几何图形中使用几何下沉的方式非常相似)。我们说三角形的第一个点是 0,然后是 1,然后是 2(这些是我们刚才描述的顶点缓冲区中顶点的索引)。非常重要的一点是,当只从一边看时,这是以顺时针顺序描述三角形的。从正面看时,点按顺时针顺序排列;当从后面看时,它们以逆时针顺序排列。
![](../Images/image055.jpg)
![](img/image055.jpg)
图 45:指数
......@@ -217,7 +217,7 @@ DirectX 根据索引缓冲区中点的顺时针或逆时针顺序确定任何特
三个 XMVECTORs 中的最后一个是眼睛的向上向量;这指定了相对于眼睛向上的方向。这里,y 轴方向为 1.0。也就是说,我们眼睛认为向上的方向与 y 轴上的正值相同。
| ![](../Images/note.png) | 注意:向上向量很重要,因为虽然我们已经定位了眼睛并描述了它正在看的点,但眼睛仍然可以自由滚动。它可以呆在同一个地方,看着同一个像素,但却是颠倒的。向上向量可以用来指定眼睛是上下颠倒的、侧躺的,或者是向上的正确方向。例如,向上向量(0.0f,-1.0f,0.0f,0.0f)意味着眼睛是颠倒的,它的顶部指向 y 轴的负值。将所有元素的向上向量设置为(0.0f,0.0f,0.0f)是没有意义的,并且会导致崩溃。在向上向量的规范中,所有负数被读取为-1.0,所有正数被读取为 1.0,0.0 被读取为 0.0。 |
| ![](img/note.png) | 注意:向上向量很重要,因为虽然我们已经定位了眼睛并描述了它正在看的点,但眼睛仍然可以自由滚动。它可以呆在同一个地方,看着同一个像素,但却是颠倒的。向上向量可以用来指定眼睛是上下颠倒的、侧躺的,或者是向上的正确方向。例如,向上向量(0.0f,-1.0f,0.0f,0.0f)意味着眼睛是颠倒的,它的顶部指向 y 轴的负值。将所有元素的向上向量设置为(0.0f,0.0f,0.0f)是没有意义的,并且会导致崩溃。在向上向量的规范中,所有负数被读取为-1.0,所有正数被读取为 1.0,0.0 被读取为 0.0。 |
两个矩阵存储在`constantBufferData`中,一个是我们刚刚看到的眼睛(视图成员),另一个是模型矩阵。模型矩阵包含绕 y 轴的旋转。如果你想让你的三角形停止旋转,你可以用`Identity`矩阵代替旋转矩阵的乘法。
......@@ -239,7 +239,7 @@ DirectX 根据索引缓冲区中点的顺时针或逆时针顺序确定任何特
```
| ![](../Images/note.png) | 注意:上一次旋转(以及 Direct3D 中的其他位置)中的角度以弧度为单位。弧度是与常数π(π)相关联的角度度量。一弧度等于(180/π)度。2π弧度等于 360 度。通过将 2π弧度(整个 360 度)乘以旋转量,可以用弧度指定任意旋转角度。例如,要旋转 50% (180 度),我们可以使用 0.5*(2π),要旋转 67.58%,我们可以使用 0.6758*(2π)。DirectXMath.h 有一些有用的常量(我们可以在下表 XM_PIDIV4 中看到其中一个)。 |
| ![](img/note.png) | 注意:上一次旋转(以及 Direct3D 中的其他位置)中的角度以弧度为单位。弧度是与常数π(π)相关联的角度度量。一弧度等于(180/π)度。2π弧度等于 360 度。通过将 2π弧度(整个 360 度)乘以旋转量,可以用弧度指定任意旋转角度。例如,要旋转 50% (180 度),我们可以使用 0.5*(2π),要旋转 67.58%,我们可以使用 0.6758*(2π)。DirectXMath.h 有一些有用的常量(我们可以在下表 XM_PIDIV4 中看到其中一个)。 |
| 常数 | 价值 | 意义 | 度 |
| XM_PI | 3.141592654 | 产品改进(Product Improve) | one hundred and eighty  |
......@@ -249,7 +249,7 @@ DirectX 根据索引缓冲区中点的顺时针或逆时针顺序确定任何特
| xm _ pidiv 2 型导弹 | 1.570796327 | PI/2 | Ninety |
| xm _ pidiv 4 型式 | 0.785398163 | PI/4 | Forty-five |
| ![](../Images/note.png) | 注意:XMVector 是来自 DirectXMath 库的向量类型(头是 DirectXMath.h)。这个库由一组帮助函数组成,以实现许多标准的数学运算。XMVector 是一个简单的结构,有四个浮点值或整数,对齐 16 个字节。DirectXMath 库已经优化了处理这种数据类型的方法(取决于硬件,库使用 SIMD 扩展来并行执行操作)。XMVector 用于许多不同的事情,包括存储和指定顶点的位置及其颜色。 |
| ![](img/note.png) | 注意:XMVector 是来自 DirectXMath 库的向量类型(头是 DirectXMath.h)。这个库由一组帮助函数组成,以实现许多标准的数学运算。XMVector 是一个简单的结构,有四个浮点值或整数,对齐 16 个字节。DirectXMath 库已经优化了处理这种数据类型的方法(取决于硬件,库使用 SIMD 扩展来并行执行操作)。XMVector 用于许多不同的事情,包括存储和指定顶点的位置及其颜色。 |
### 原始拓扑
......@@ -362,7 +362,7 @@ DirectX 根据索引缓冲区中点的顺时针或逆时针顺序确定任何特
如前所述,索引缓冲区多次引用相同的顶点来描述相邻的三角形是正常的。如果两个三角形直接在彼此旁边,并且它们共享一条线,我们不需要存储六个顶点(两个三角形中的每一个对应一个点)。我们可以存储四个顶点,并使用索引缓冲区描述两个相邻的三角形,重用两个顶点:
![](../Images/image056.png)
![](img/image056.png)
图 46:形成正方形的三角形
......@@ -372,7 +372,7 @@ DirectX 根据索引缓冲区中点的顺时针或逆时针顺序确定任何特
`Render`方法中,有一个对`IASetPrimitiveTopology`的调用,它要求 GPU 将点渲染为三角形。渲染高度图的另一个好方法是使用线条。您可以将此方法的参数从`D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST`更改为`D3D11_PRIMITIVE_TOPOLOGY_LINELIST`,您的高度图将呈现为彩色线条的集合。
| ![](../Images/tip.png) | 提示:在前面的例子中,我使用局部数组将索引和顶点存储在系统内存中;即使是中等大小的高度图,这也会很快导致堆栈溢出。函数的本地参数存储在堆栈上,这样,当函数返回时,为它们分配的内存将自动清除。如果需要更大的高度图,应该考虑将堆与“new”运算符一起使用。 |
| ![](img/tip.png) | 提示:在前面的例子中,我使用局部数组将索引和顶点存储在系统内存中;即使是中等大小的高度图,这也会很快导致堆栈溢出。函数的本地参数存储在堆栈上,这样,当函数返回时,为它们分配的内存将自动清除。如果需要更大的高度图,应该考虑将堆与“new”运算符一起使用。 |
## 第十九章:投射选项
......@@ -424,7 +424,7 @@ XMMatrixPerspectiveFovRH
默认情况下,Direct3D 模板呈现用透视渲染的立方体。有时,当数据不以这种方式呈现时,更容易理解数据。毕竟透视是一种扭曲效应,可能会模糊数据的含义。正投影与透视投影相同,不同的是,尽管对象与相机相距很远,但它们的外观尺寸保持不变。这看起来有点不自然,但这可能是一种非常清晰的数据呈现方式。距离较远的数据点不会比距离较近的数据点显得更小,也更容易比较。远处的数据和近处的数据一样清晰,而通过透视投影,远处的数据在接近地平线上的一个点时被挤压在一起。有关这些投影之间差异的描述,请参见图 47。
![](../Images/image057.png)
![](img/image057.png)
图 47:投影选项
......@@ -575,4 +575,4 @@ XMMatrixPerspectiveFovRH
这些代码大部分应该都很熟悉。我已经创建了一个顶点缓冲区,一个索引缓冲区,并渲染了数据。这是渲染 100 个彩色三角形的演示。可以通过更改包含 count = 100 的行来增加数量。您应该会发现,您可以增加的三角形数量远远超过您可以使用 Direct2D 几何图形绘制的三角形数量。
| ![](../Images/tip.png) | 提示:我为代码示例中的每个三角形添加了一个缓慢递增的 z 值。这样做是为了不让两个三角形处于完全相同的位置。如果两个三角形位于完全相同的位置,图形处理器有时会先渲染一个,有时会先渲染另一个三角形。这导致令人不快的闪烁伪像。通过在稍微不同的 z 平面上创建每个三角形,我们消除了这种闪烁。 |
\ No newline at end of file
| ![](img/tip.png) | 提示:我为代码示例中的每个三角形添加了一个缓慢递增的 z 值。这样做是为了不让两个三角形处于完全相同的位置。如果两个三角形位于完全相同的位置,图形处理器有时会先渲染一个,有时会先渲染另一个三角形。这导致令人不快的闪烁伪像。通过在稍微不同的 z 平面上创建每个三角形,我们消除了这种闪烁。 |
\ No newline at end of file
......@@ -12,7 +12,7 @@
当使用三维笛卡尔坐标系时,可以选择每个轴相对于彼此指向哪个方向。任意两个轴定义一个二维平面。例如,X 轴和 Y 轴定义一个平面,而 Z 轴和 X 轴定义另一个平面。如果你想象一台摄像机的方向是 X 轴和 Y 轴定义了一个平行于监视器的平面,Y 轴指向上,X 轴指向右,那么就可以选择 Z 轴指向哪个方向。它可以指向屏幕内外。记忆这两个坐标系常用的助记符是惯用手,即右手坐标和左手坐标。当你以与*图 2.1* 中描述的相同方式握住你的手时,手指指向轴的正方向。
![figure 2-1.png](../Images/image001.png)
![figure 2-1.png](img/image001.png)
图 2.1:左手和右手坐标
......@@ -24,7 +24,7 @@
模型通常使用三维建模应用程序作为单独的资产创建。我在这本书的例子中使用了 Blender 搅拌机可从[http://www.blender.org/](http://www.blender.org/)下载。模型可以从建模应用程序导出到文件中,并加载到我们的程序中。当模型在三维建模器中设计时,它们是用自己的本地原点设计的。例如,如果您设计了一个表格模型,它可能看起来像建模应用程序中的*图 2.2*
![figure 2-2.png](../Images/image002.png)
![figure 2-2.png](img/image002.png)
图 2.2:搅拌机建模器中的表格
......@@ -32,7 +32,7 @@
*图 2.3* 显示的是同型号的另一张截图,不过现在已经放入了一个房间。
![figure 2-3.png](../Images/image003.png)
![figure 2-3.png](img/image003.png)
图 2.3:世界空间中的表格
......@@ -40,7 +40,7 @@
一旦对象相对于世界原点定位,表示世界坐标空间的最后一步是在虚拟世界的某个点放置摄像机或眼睛。在三维图形中,摄像机被定位并给定一个面向的方向。摄像机看到虚拟世界中有一个非常特殊形状的区域。该形状称为平截头体。平截头体是几何形状的一部分,通常是位于切割该形状的两个平行平面之间的金字塔或圆锥。三维图形中的平截头体是一个正方形的底部金字塔形状,其顶点在摄像机处。金字塔在近、远剪裁平面处切割(*图 2.4* )。
![figure 2-4.png](../Images/image004.png)
![figure 2-4.png](img/image004.png)
图 2.4:平截头体
......@@ -48,7 +48,7 @@
*图 2.5* 显示了将三维形状投影到二维平面上的图示。在 DirectX 中,投影的实际过程不过是少数矩阵乘法,但图示可能有助于将运算概念化。
![figure 2-5.png](../Images/image005.png)
![figure 2-5.png](img/image005.png)
图 2.5:三维投影
......@@ -64,7 +64,7 @@
要创建 100%红色、13%绿色和 25%蓝色的红色,我们可以使用(`1.0f, 0.13f, 0.25f`)。
![](../Images/image006.png)
![](img/image006.png)
如果存在,alpha 分量通常用于透明度。在本书中,我们不会使用 alpha 通道,它的值无关紧要,但我会将其设置为 100%或 1.0f。
......@@ -74,7 +74,7 @@
在早期版本的 DirectX 中,管道是固定的,它是由应用编程接口的程序员设计的一组预先设计的阶段。程序员可以选择几个选项来改变图形处理器渲染最终图形的方式,但整个过程基本上是一成不变的。今天的图形管道非常灵活,它具有许多可直接编程的阶段。这意味着管道要复杂得多,但也更加灵活。*图 2.6* 是当前 DirectX 11 图形流水线各阶段的大致轮廓。
![figure 2-6.png](../Images/image007.png)
![figure 2-6.png](img/image007.png)
图 2.6: DirectX 11 图形管道
......@@ -130,7 +130,7 @@
我们使用 z 缓冲器来解决这个问题。z 缓冲区是一个二维数组,通常由浮点值组成。这些值指示从管道的光栅化器阶段中当前光栅化的每个像素到观看者的距离。当图形处理器渲染离观察者一定距离(Z)的对象的像素时,它首先检查当前像素的 Z 是否比它先前渲染的 Z 更近。如果像素已经被渲染,并且对象上次更接近,则不需要渲染新像素;否则像素应该被更新。
![figure 2-7.png](../Images/image008.png)
![figure 2-7.png](img/image008.png)
图 2.7:深度缓冲区和面
......
......@@ -6,17 +6,17 @@
**打开** Visual Studio 2012,新建 **Direct2D App (XAML)** 项目。我已经在截图中命名了我的项目 dxgamepidering(*图 3.1* )。请记住,如果您为项目使用不同的名称,您应该在代码中重命名对`DXGameProgramming`命名空间的所有引用。
![figure 3.1.png](../Images/image009.png)
![figure 3.1.png](img/image009.png)
图 3.1:启动新的 Direct2D 应用程序(XAML)项目
| ![](../Images/note.png) | 注意:我在这本书里的所有代码都是基于 Direct2D 应用(XAML)模板的。这个模板设置了一个同时使用二维和三维的应用程序。我们将主要关注 Direct3D,但是 Direct2D 在创建三维应用程序时也非常重要。Direct2D 用于渲染平视显示器(HUD)、玩家分数、各种其他精灵以及可能的背景。 |
| ![](img/note.png) | 注意:我在这本书里的所有代码都是基于 Direct2D 应用(XAML)模板的。这个模板设置了一个同时使用二维和三维的应用程序。我们将主要关注 Direct3D,但是 Direct2D 在创建三维应用程序时也非常重要。Direct2D 用于渲染平视显示器(HUD)、玩家分数、各种其他精灵以及可能的背景。 |
### 对 DirectXPage.xaml 的更改
应用程序的主 XAML 页面有一些我们不需要的控件,这些可以删除。双击 DirectXPage。解决方案资源管理器中的 XAML 文件。这将在 Visual Studio 的 XAML 页面设计器中打开该页面。右键单击对象并从上下文菜单中选择删除,删除显示“你好,XAML”的文本控件(参见图 3.2)。
![figure 3.2.png](../Images/image011.png)
![figure 3.2.png](img/image011.png)
图 3.2:删除你好,XAML
......
......@@ -230,7 +230,7 @@
上面的代码为 CPU 的常量缓冲区设置了视图矩阵。我们将使用`XMMatrixLookAtRH`所以我们的坐标系将是右旋的。这些参数定义了摄像机的位置、它看向哪一点以及摄像机的向上矢量。图 4.1 说明了这三个向量的含义。
![figure 4-1.png](../Images/image012.png)
![figure 4-1.png](img/image012.png)
图 4.1: LookAtMatrix
......@@ -254,11 +254,11 @@ B 点定义了摄像机正朝向的点;它决定了相机面对的方向。这
了解顶点着色器如何工作的最好方法是创建一个顶点着色器。要向项目中添加新的顶点着色器,请在解决方案资源管理器中右键单击项目名称,并从上下文菜单中选择**添加新项目**(参见*图 4.2* )。
![figure 4-2.png](../Images/image013.png)
![figure 4-2.png](img/image013.png)
图 4.2:添加新项目
![figure 4-3.png](../Images/image014.png)
![figure 4-3.png](img/image014.png)
图 4.3:添加顶点着色器
......@@ -347,7 +347,7 @@ B 点定义了摄像机正朝向的点;它决定了相机面对的方向。这
此时,您应该能够运行您的应用程序。看起来不会有什么不同,但是如果你导航到输出目录,你会看到两个新文件已经编译好了:VertexShader.cso 和 PixelShader.cso(见*图 4.4* )。
![figure 4-4.png](../Images/image015.png)
![figure 4-4.png](img/image015.png)
图 4.4:客户服务组织文件
......
......@@ -6,7 +6,7 @@
有许多三维文件格式,每种格式都有其优缺点。在本章中,我们将研究一种简单但灵活的格式,这种格式相当通用和标准化。它被称为目标文件格式,最初由波前技术公司开发。目标文件扩展名为“obj”。我们将编写一个类来解析目标文件,并根据其中的顶点信息创建网格。
| ![](../Images/note.png) | 注意:网格是形成三维模型的三角形的集合。网格和模型在当前上下文中是可互换的术语。在 DirectX 的过去版本中有一个网格接口,但是 DirectX 11 不包含这个接口,所以网格必须手动加载和操作。这是一点额外的工作,但它更灵活。 |
| ![](img/note.png) | 注意:网格是形成三维模型的三角形的集合。网格和模型在当前上下文中是可互换的术语。在 DirectX 的过去版本中有一个网格接口,但是 DirectX 11 不包含这个接口,所以网格必须手动加载和操作。这是一点额外的工作,但它更灵活。 |
目标文件格式有几个优点。它是纯文本,易于阅读和解析。这些文件是人类可读和可写的;它们不是二进制的。人类可读的纯文本需要更多的存储空间,并且通常比二进制格式更慢。例如,值 3.14159f 在纯文本中取 8 个字节,但在二进制中作为 32 位浮点只有 4 个字节。对于我们将要处理的小模型来说,数据的大小可以忽略不计,CPU 将文本解析为变量所需的时间也是如此。对于模型更复杂或有很多模型的大型项目,您可以考虑使用二进制文件格式或编写自己的格式。
......@@ -14,13 +14,13 @@
该格式的另一个缺点是顶点缓冲区没有以有效的方式进行索引。在上一本书中,我们研究了在 GPU 上使用索引缓冲区多次引用顶点,这为我们节省了一些 GPU 内存。我们将检查的方法不推荐用于复杂的几何图形,因为如果不使用索引拓扑,图形处理器必须存储比实际需要多很多倍的顶点。例如,我们只需要 8 个顶点就可以在图形处理器上存储一个立方体。我们可以使用索引缓冲区高效地索引顶点。然而,我们将要加载的对象文件将一个立方体存储为 12 个顶点:每个三角形 3 个顶点,每个面 2 个三角形,以及 6 个面。
| ![](../Images/note.png) | 注意:当您对这种简单的基于文本的 OBJ 格式感到满意时,您可以开发自己的格式,专门用于以最适合独特环境的方式存储模型。我们正在研究 OBJ 文件格式,作为阅读基本模型的一个例子。您可以轻松地编写自己的自定义三维文件格式,它可以按照您的需要准确地存储信息,并且比 OBJ 格式更小,解析速度更快。 |
| ![](img/note.png) | 注意:当您对这种简单的基于文本的 OBJ 格式感到满意时,您可以开发自己的格式,专门用于以最适合独特环境的方式存储模型。我们正在研究 OBJ 文件格式,作为阅读基本模型的一个例子。您可以轻松地编写自己的自定义三维文件格式,它可以按照您的需要准确地存储信息,并且比 OBJ 格式更小,解析速度更快。 |
### 向项目添加模型
目标文件是用一种小的脚本语言存储的,语法非常简单。它是一个纯文本文件,包含顶点位置和其他信息的列表或数组。数组的每个元素都在自己的行中指定。有顶点位置、纹理坐标、顶点法线和面的数组。这里的面是一个平面。面由引用每个数组(顶点位置、纹理坐标或法线数组)中的值的索引集合定义。面定义可以引用每个数组中的任何值,具有很大的灵活性。还有许多其他的关键字可以用在目标文件中。以下只是对我们将在程序中使用的程序的简要描述。
![figure 5-1.png](../Images/image017.png)
![figure 5-1.png](img/image017.png)
图 5.1:搅拌机里的宇宙飞船
......@@ -188,7 +188,7 @@
由于 obj 扩展名,Visual Studio 将假设该文件是一个常规的可重定位的机器代码对象文件。这样不行,我们的文件是模型,不是二进制代码文件。右键单击解决方案资源管理器中的**宇宙飞船. obj** 文件,并从上下文菜单中选择**属性**。您将看到文件属性表单,您可以在其中将项目**类型值****对象**更改为**文本**(参见*图 5.2* )。
![figure 5-2.png](../Images/image018.png)
![figure 5-2.png](img/image018.png)
图 5.2:更改对象文件项目类型
......@@ -232,14 +232,14 @@
上面的线指定了三个顶点,每个顶点都有自己的位置、纹理坐标和法线。这些顶点在我们的模型中创建了一个三维三角形。第一个元素,我把它涂成蓝色,16/34/2,表示第 16 <sup></sup>顶点位置,第 34 <sup></sup>纹理坐标,以及指定的第 2 <sup></sup>法线。
| ![](../Images/note.png) | 注:索引使用基于“1”的计数系统引用数组。每个数组中的第一个元素的下标为 1。C++引用数组使用基于“0”的系统,因此当我们的程序读取这些行时,我们必须能够从每个索引中减去一个,以便在两个系统之间转换。或者,我们可以在位置 0 向 C++数组添加一个伪值,它永远不会被面引用。 |
| ![](../Images/note.png) | 注意:目标文件可以附带一个带有 MTL 扩展名的材料文件。该文件描述了 OBJ 文件中描述的每个对象所使用的材料。对于我们的目的,材料将被硬编码,除了紫外线纹理坐标。 |
| ![](img/note.png) | 注:索引使用基于“1”的计数系统引用数组。每个数组中的第一个元素的下标为 1。C++引用数组使用基于“0”的系统,因此当我们的程序读取这些行时,我们必须能够从每个索引中减去一个,以便在两个系统之间转换。或者,我们可以在位置 0 向 C++数组添加一个伪值,它永远不会被面引用。 |
| ![](img/note.png) | 注意:目标文件可以附带一个带有 MTL 扩展名的材料文件。该文件描述了 OBJ 文件中描述的每个对象所使用的材料。对于我们的目的,材料将被硬编码,除了紫外线纹理坐标。 |
### 搅拌机导出设置
如果你使用的是 Blender 2.66,或者是类似的版本,有相同的 OBJ 导出器,你可能会想要导出你自己的模型,其设置与我用来导出这艘宇宙飞船的设置相同。为 OBJ 文件导出选择类似的选项将创建可以使用我们的`ModelReader`类加载的模型。*图 5.3* 显示了 Blender OBJ 出口商的屏幕截图。左侧面板是更改这些设置的地方。
![figure 5-3.png](../Images/image019.png)
![figure 5-3.png](img/image019.png)
图 5.3:设置 OBJ 模型导出选项
......@@ -268,9 +268,9 @@
| 向上 | 向上 |
| 路径模式 | 汽车 |
| ![](../Images/note.png) | 注意:对象文件可以包含多个对象。在我们的例子中,只有宇宙飞船模型,但是格式允许在一个文件中定义许多模型。文件中的每个对象都有一个名称,每个对象都有自己的顶点、法线、纹理坐标和面的列表。如果有多个对象,则在“o”关键字后指定每个对象。“o”关键字后面是特定对象的名称。直到下一个“o”关键字的所有顶点信息都对应于最后一个命名的对象。 |
| ![](img/note.png) | 注意:对象文件可以包含多个对象。在我们的例子中,只有宇宙飞船模型,但是格式允许在一个文件中定义许多模型。文件中的每个对象都有一个名称,每个对象都有自己的顶点、法线、纹理坐标和面的列表。如果有多个对象,则在“o”关键字后指定每个对象。“o”关键字后面是特定对象的名称。直到下一个“o”关键字的所有顶点信息都对应于最后一个命名的对象。 |
| ![](../Images/note.png) | 注意:OBJ 文件语法允许指定非三角形面。例如,正方形面可以用四个顶点构成,五边形面可以用五个顶点构成。这种格式允许更简洁的面部定义。当使用此功能加载模型时,面必须转换为三角形的集合,因为 DirectX 使用三角形列表渲染网格。在下面的例子中,我假设面都是三角形,并使用“三角化面”选项导出模型,该选项将在许多三维建模应用程序中可用。 |
| ![](img/note.png) | 注意:OBJ 文件语法允许指定非三角形面。例如,正方形面可以用四个顶点构成,五边形面可以用五个顶点构成。这种格式允许更简洁的面部定义。当使用此功能加载模型时,面必须转换为三角形的集合,因为 DirectX 使用三角形列表渲染网格。在下面的例子中,我假设面都是三角形,并使用“三角化面”选项导出模型,该选项将在许多三维建模应用程序中可用。 |
## 模型类
......@@ -558,6 +558,6 @@
现在当你运行应用程序的时候,你会在*图 5.4* 中看到彩色飞船。这看起来有点奇怪,因为它是用随机颜色的三角形渲染的。实际上仅应用原始模型中的顶点。
![figure 5-4.png](../Images/image020.png)
![figure 5-4.png](img/image020.png)
图 5.4:带有顶点的宇宙飞船模型
\ No newline at end of file
......@@ -8,7 +8,7 @@
二维图像包裹在网格周围,以创建复杂的彩色对象的错觉。二维图像中的像素通常使用所谓的纹理元素或紫外坐标来引用。不使用 X 和 Y,用 U 和 V 分量描述纹理元素坐标。U 分量与 X 相同,因为它代表水平轴,V 分量类似于 Y 轴,代表垂直轴。紫外坐标和标准笛卡尔 XY 坐标的主要区别在于,紫外坐标将图像中的参考位置作为 0.0 到 1.0 之间的归一化值。点(0.0,0.0)引用图像的左上角,点(1.0,1.0)引用图像的右下角。图像中的每个点都有一个介于(0.0,0.0)和(1.0,1.0)之间的紫外坐标。*图 6.1* 是一种可以应用于视频游戏地面的草状纹理。纹理中的几个点被高亮显示,以显示紫外线值的坐标。
![figure 6-1.png](../Images/image021.png)
![figure 6-1.png](img/image021.png)
图 6.1:纹理元素/紫外线坐标
......@@ -18,21 +18,21 @@
将矩形或正方形纹理映射到三维矩形对象很容易,但是将二维纹理映射到网格,就像我们上一章的宇宙飞船一样,有点棘手。最好使用三维建模应用程序从网格导出紫外线布局。三维建模软件还能够生成和导出参考布局的紫外坐标。我们在上一章的目标文件代码中看到了这些坐标。我已经使用 Blender 导出了我们飞船模型的 UV 布局,如图*图 6.2*;这是 spaceship.png 紫外线布局。UV 布局是模型中面轮廓的集合。我们可以使用标准的二维图像编辑器来绘制这些形状。这些形状在宇宙飞船. obj 文件中被引用为 VT 坐标。
![figure 6-2.png](../Images/image022.png)
![figure 6-2.png](img/image022.png)
图 6.2:飞船紫外线布局
*图 6.2* 中的每一个形状都对应着飞船中的一张脸。我已经使用 Gimp 2.8.4 创建了本书中的纹理。Gimp 可从([http://www.gimp.org/](http://www.gimp.org/)获得。如果你想为宇宙飞船创建你自己的纹理,把上面的图像复制到一个二维图像应用程序中,比如 Gimp,并把它保存为 spaceship.png。在*图 6.3 中,*我使用蓝色金属纹理给形状上色。
| ![](../Images/note.png) | 注意:本书中的纹理是作为 PNG 图像文件导出的。PNG 很好,因为它使用无损压缩,所以纹理不会像 JPEG 那样被压缩伪像损坏,但是它们比标准位图文件小。巴布亚新几内亚也支持透明度。当你从书中复制图像时,你可能会发现颜色不同。例如,背景可以是黑色的,而不是透明的。背景没有被 OBJ 文件中的紫外坐标映射或引用,也不可见。 |
| ![](img/note.png) | 注意:本书中的纹理是作为 PNG 图像文件导出的。PNG 很好,因为它使用无损压缩,所以纹理不会像 JPEG 那样被压缩伪像损坏,但是它们比标准位图文件小。巴布亚新几内亚也支持透明度。当你从书中复制图像时,你可能会发现颜色不同。例如,背景可以是黑色的,而不是透明的。背景没有被 OBJ 文件中的紫外坐标映射或引用,也不可见。 |
![figure 6-3.png](../Images/image023.png)
![figure 6-3.png](img/image023.png)
图 6.3:纹理化紫外线布局
*图 6.3* 中,我已经把 UV 涂成了蓝钢质感。从*图 6.3* 复制并保存新的纹理化紫外线布局,或者根据*图 6.2* 中的紫外线布局创建自己的纹理化紫外线布局。将纹理设计或保存为**spaceship.png**后,在解决方案资源管理器中右键单击项目名称,选择**添加现有项目**,定位 PNG 文件,将图像导入到我们的 Visual Studio 项目中。
| ![](../Images/note.png) | 注意:图 6.3 的原始纹理是从 Blender 导出到 1024x1024 PNG 图像的。二维纹理使用 alpha 通道消耗宽度*高度*3 字节的内存或宽度*高度*4 字节的内存。适中的 128x128 或 256x256 PNG 应该足够大,以保持纹理的细节,但又足够小,以免消耗运行应用程序的便携式设备的所有内存。UV 坐标使用从 0.0 到 1.0 的百分比,因此纹理可以缩放以适应目标设备的内存限制,并且模型网格中引用的 UV 坐标不需要更改。 |
| ![](img/note.png) | 注意:图 6.3 的原始纹理是从 Blender 导出到 1024x1024 PNG 图像的。二维纹理使用 alpha 通道消耗宽度*高度*3 字节的内存或宽度*高度*4 字节的内存。适中的 128x128 或 256x256 PNG 应该足够大,以保持纹理的细节,但又足够小,以免消耗运行应用程序的便携式设备的所有内存。UV 坐标使用从 0.0 到 1.0 的百分比,因此纹理可以缩放以适应目标设备的内存限制,并且模型网格中引用的 UV 坐标不需要更改。 |
## 从文件中读取纹理
......@@ -155,7 +155,7 @@
我们还需要创建一个`ID3D11ShaderResourceView`。一个资源可以由多个子资源组成;一个`ID3D11ShaderResourceView`用于指定着色器可以使用哪些子资源,以及如何读取资源中的数据。
| ![](../Images/note.png) | 注意:您将在上面的代码表中看到对 MIP 映射的引用。MIP 贴图,源自拉丁语“parvo 中的 multum”或多或少,是由相同纹理在几个细节层次上组成的纹理。MIP 级别用于提高性能。当三维物体的观察者非常靠近物体时,可以应用最详细的纹理,而当观察者远离物体时,可以使用不太详细的纹理。MIP 地图比我们在这里做的更高级,但是在渲染复杂场景时,它们的性能要好得多。 |
| ![](img/note.png) | 注意:您将在上面的代码表中看到对 MIP 映射的引用。MIP 贴图,源自拉丁语“parvo 中的 multum”或多或少,是由相同纹理在几个细节层次上组成的纹理。MIP 级别用于提高性能。当三维物体的观察者非常靠近物体时,可以应用最详细的纹理,而当观察者远离物体时,可以使用不太详细的纹理。MIP 地图比我们在这里做的更高级,但是在渲染复杂场景时,它们的性能要好得多。 |
## 应用纹理 2D
......@@ -433,7 +433,7 @@
```
| ![](../Images/note.png) | 注意:根据微软的说法,您不需要使用采样器状态。如果代码不包含自己的状态,将会假定有一个默认状态。问题是,包括 Windows RT Surface 在内的一些机器默认情况下似乎不包含任何采样器状态,如果您自己不指定采样器状态,那么在某些设备上调试时,您最终可能会看到一个黑色的、未经纹理处理的模型。 |
| ![](img/note.png) | 注意:根据微软的说法,您不需要使用采样器状态。如果代码不包含自己的状态,将会假定有一个默认状态。问题是,包括 Windows RT Surface 在内的一些机器默认情况下似乎不包含任何采样器状态,如果您自己不指定采样器状态,那么在某些设备上调试时,您最终可能会看到一个黑色的、未经纹理处理的模型。 |
采样器状态是设备资源。打开 **SimpleTextRenderer.cpp** 文件,在`CreateDeviceResources`方法结束时创建采样器状态。下面的代码表中突出显示了附加代码。
......@@ -486,6 +486,6 @@
此时,您应该能够运行应用程序并看到一个改进的模型宇宙飞船,带有我们之前绘制的蓝色纹理(图 6.4 )。
![figure 6-4.png](../Images/image025.png)
![figure 6-4.png](img/image025.png)
图 6.4:纹理飞船
\ No newline at end of file
......@@ -21,7 +21,7 @@ HLSL 也有特殊的数据类型和功能,旨在使三维编程更容易。GPU
* 浮点:32 位 IEEE 浮点值。
* double: 64 位 IEEE 浮点值。
| ![](../Images/note.png) | 注意:16 位 IEEE 半浮点型是压缩 32 位浮点的一种方式。精度降低后,32 位浮点可以压缩到 16 位。该数据类型非常适合存储大量数据,但现在已被弃用,建议您不要使用它。 |
| ![](img/note.png) | 注意:16 位 IEEE 半浮点型是压缩 32 位浮点的一种方式。精度降低后,32 位浮点可以压缩到 16 位。该数据类型非常适合存储大量数据,但现在已被弃用,建议您不要使用它。 |
### 语义名称
......
......@@ -8,7 +8,7 @@
在我们看灯光之前,我们需要看法线。法线是垂直于另一个向量或垂直于一个表面的向量。*图 8.1* 中的图像描绘了法线渲染为箭头的立方体。立方体有八个向量,其中一个在图像中不可见(最左下方的向量)。这些向量共同描述了立方体的六个面,其中只有三个是可见的。白色箭头是矢量法线;立方体中的每个向量都有一个,它们从立方体的中心指向角的方向。黑色箭头是表面法线;它们代表立方体的每个面所指向的方向。
![figure 8-1.png](../Images/image026.png)
![figure 8-1.png](img/image026.png)
图 8.1:具有矢量和表面法线的立方体
......@@ -373,11 +373,11 @@
运行应用程序时,应该又看到了飞船,只是现在它有了简单的漫反射灯光明暗效果(*图 8.2* )。
![figure 8-2.png](../Images/image027.png)
![figure 8-2.png](img/image027.png)
图 8.2:带漫射照明的宇宙飞船
| ![](../Images/note.png) | 注意:我已经对灯光的变量进行了硬编码。更常见的是将这些放入`cbuffer`中,以便中央处理器可以更新和更改这些值。根据项目被访问的频率将它们分组到不同的`cbuffers`中是很常见的。例如,您可能有`perobject``perframe``pergame``cbuffers`,它们分别保存游戏中每个对象、每帧或仅一次必须更新的数据。我使用了单个 cbuffer,因为我们所做的不是处理器密集型的。 |
| ![](img/note.png) | 注意:我已经对灯光的变量进行了硬编码。更常见的是将这些放入`cbuffer`中,以便中央处理器可以更新和更改这些值。根据项目被访问的频率将它们分组到不同的`cbuffers`中是很常见的。例如,您可能有`perobject``perframe``pergame``cbuffers`,它们分别保存游戏中每个对象、每帧或仅一次必须更新的数据。我使用了单个 cbuffer,因为我们所做的不是处理器密集型的。 |
上述漫射照明方程的基础相当简单;如果面的法线正对着光,则表面被描绘为纹理的颜色。如果面的法线不面向漫射光源,则表面呈现为发射色和环境色。
......
......@@ -205,11 +205,11 @@ SimpleTextRenderer.cpp 文件中方法的主体(`KeyDown`和`KeyUp`)非常简单
双击**直接打开页面。XAML** 并在 XAML 页面添加四个`Ellipse`控件(见*图 9.1* )。这些将是我们的 dpad 添加省略号的位置或省略号的大小并不重要,因为我们将在 XAML 代码视图中更改这些设置。
![figure 9-1.png](../Images/image028.png)
![figure 9-1.png](img/image028.png)
图 9.1:为 DPad 添加椭圆
| ![](../Images/note.png) | 注意:我在我们的 DPad 中使用了椭圆控件而不是 XAML 按钮。XAML 按钮的后台已经写了很多代码。例如,它们具有处理被按下和释放的指针的功能。这些事件是自动处理的,不会传递给我们的自定义事件处理程序。使用椭圆控件很容易捕捉指针的按压和释放。 |
| ![](img/note.png) | 注意:我在我们的 DPad 中使用了椭圆控件而不是 XAML 按钮。XAML 按钮的后台已经写了很多代码。例如,它们具有处理被按下和释放的指针的功能。这些事件是自动处理的,不会传递给我们的自定义事件处理程序。使用椭圆控件很容易捕捉指针的按压和释放。 |
现在我们已经添加了省略号,可以在 XAML 代码视图中更改设置。这些更改在下面的代码表中突出显示。
......@@ -227,7 +227,7 @@ SimpleTextRenderer.cpp 文件中方法的主体(`KeyDown`和`KeyUp`)非常简单
我捕捉到的事件有`PointerPressed``PointerReleased`。如果你是为鼠标编程的话,我捕捉到的另外两个事件`PointerEntered``PointerExited`,意义不大。我捕捉到了这些事件,因此如果用户用触摸屏按住指针,并将手指滑动到另一个椭圆上,则`PointerPressed`事件不会触发。这对触摸屏没有好处。这个硬币的另一面是,如果鼠标光标没有被按下,而是进入或退出省略号,`PointerEntered``PointerExited`事件不应触发。我已经捕获了事件,并区分了事件处理程序中的指针类型。我已经通过与`PointerReleased`相同的处理程序路由了`PointerExited`事件,因为它们做完全相同的事情。要创建事件处理程序,右键单击 XAML 代码中的方法名称,并从上下文菜单中选择**导航到事件处理程序**。Visual Studio 将编写事件处理程序,并在代码中带我们找到它(参见*图 8.2* )。
![figure 9-2.png](../Images/image029.png)
![figure 9-2.png](img/image029.png)
图 8.2:添加事件处理程序
......
......@@ -69,7 +69,7 @@
这个物体的纹理,叫做 baddie.png,是下面的紫外线贴图(图 10.1 )。
>![figure 10-1.png](../Images/image030.png)
>![figure 10-1.png](img/image030.png)
图 10.1: baddie.png
......@@ -137,7 +137,7 @@
子弹的 UV 布局如下图(*图 10.2* )。
![figure 10-2.png](../Images/image031.png)
![figure 10-2.png](img/image031.png)
图 10.2: bullet.png
......@@ -314,7 +314,7 @@
我们将通过在三维物体后面绘制一个星域的二维图像,将我们的宇宙飞船放置在太空的中间。下面的图片(*图 10.3* )应该保存为**starfieldbackground.png**并导入到您的项目中,就像我们之前对 UV 纹理所做的那样。它将构成我们游戏的背景。
![figure 10-3.png](../Images/image032.png)
![figure 10-3.png](img/image032.png)
图 10.3: starfieldbackground.png
......@@ -767,7 +767,7 @@
最后,也是最重要的一步,点击 F5 进行调试。你应该看到一个三维空间射手(*图 10.3* )。
![figure 10-4.png](../Images/image033.png)
![figure 10-4.png](img/image033.png)
图 10.3:太空射手
......
......@@ -4,7 +4,7 @@
在讨论如何从电子邮件的每个部分提取信息之前,我们应该理解邮箱可以被视为一个半结构化的数据库,它不使用本地查询语言(例如,SQL)来提取信息。
![](../images/00003.gif)
![](img/00003.gif)
*表 1:典型的邮件结构*
......@@ -36,7 +36,7 @@ MailKit 是跨平台的。基于 MimeKit 构建的 IMAP、POP3 和 SMTP 的. NET
您可以使用 Visual Studio 将 MailKit 作为 NuGet 包安装。
![](../images/00004.jpeg)
![](img/00004.jpeg)
*图 1:用 Visual Studio 2015* 将 MailKit 作为 NuGet 包安装
......@@ -369,7 +369,7 @@ MailKit 是一个非常棒的、易于使用的处理电子邮件的库。让我
当这段代码运行时,生成的输出类似于图 2。
![](../images/00005.gif)
![](img/00005.gif)
*图 2:邮件头数据输出*
......@@ -892,7 +892,7 @@ MailKit 的一个很棒的特性是它的每个电子邮件标题都有一个字
这个演示程序根据以前收到的电子邮件的内容发送一封自动回复电子邮件。
![](../images/00006.jpeg)
![](img/00006.jpeg)
*图 3:根据收到的邮件内容自动回复邮件*
......
......@@ -22,7 +22,7 @@ EmguCV 允许从本机调用 OpenCV 函数。NET 用 C#、VB、VC++,甚至 Iro
您可以使用 Visual Studio 将 EmguCV 作为 NuGet 包安装。因为 NuGet 上有几种实现,所以我们将使用 EmguCV.221.x64。如果您在 32 位操作系统上开发,您可以安装 EmguCV.221.x86。
![](../images/00007.jpeg)
![](img/00007.jpeg)
*图 4:用 Visual Studio 2015* 将 EmguCV 作为 NuGet 包安装
......@@ -38,7 +38,7 @@ EmguCV 允许从本机调用 OpenCV 函数。NET 用 C#、VB、VC++,甚至 Iro
如果我们不需要执行光学字符识别,我们可以简单地使用 NuGet 安装的东西,以及下面的 Emgu 通用设置。简历就没必要了。
![](../images/00008.jpeg)
![](img/00008.jpeg)
*图 5:EmguCV*通用窗口设置
......@@ -48,7 +48,7 @@ EmguCV 允许从本机调用 OpenCV 函数。NET 用 C#、VB、VC++,甚至 Iro
图 6 描述了添加这些 dll 后,Visual Studio 项目引用应该是什么样子。
![](../images/00009.jpeg)
![](img/00009.jpeg)
*图 6:带有 EmguCV DLLs 的 Visual Studio 项目*
......
......@@ -18,7 +18,7 @@
图 7 显示了 RestSharp 如何作为 NuGet 包安装。
![](../images/00010.jpeg)
![](img/00010.jpeg)
*图 7:用 Visual Studio 2015 将 RestSharp 作为 NuGet 包安装*
......@@ -143,7 +143,7 @@
让我们以《纽约时报》(NYT)API 为例来检查代码。在网络解析器构造函数中,通过传递站点的应用编程接口地址的基本网址来创建一个 RestClient 实例。通过查看[图书畅销书 API 文档](http://developer.nytimes.com/docs/books_api/Books_API_Best_Sellers),我们可以看到这个 API 的基本网址是[http://api.nytimes.com/svc/books](http://api.nytimes.com/svc/books)
![](../images/00011.jpeg)
![](img/00011.jpeg)
*图 8:畅销书《纽约时报》图书 API 基本 URL*
......@@ -153,7 +153,7 @@
如果你四处看看,用 [API 控制台](http://developer.nytimes.com/io-docs)做实验,你会更好地理解 API。
![](../images/00012.jpeg)
![](img/00012.jpeg)
*图 9:纽约时报图书 API 控制台*
......@@ -256,13 +256,13 @@
```
![](../images/00013.jpeg)
![](img/00013.jpeg)
*图 10:NYT 畅销书排行榜名产生于 Raw JSON。*
现在我们已经看到了如何获取原始的 JSON 结果,我们可以通过将 JSON 数据序列化并结构化为具有特定字段的对象来最好地理解它。请记住,JSON 数据已经是响应的对象表示,但是它是作为字符串返回的,这意味着它可以序列化为具有特定属性的类。实现这一点的一个简单方法是使用流行的[JSON.NET](http://www.newtonsoft.com/json)库。JSON.NET 可以通过 NuGet 安装。
![](../images/00014.jpeg)
![](img/00014.jpeg)
*图 11:通过 NuGet 安装的 JSON.NET。*
......@@ -270,7 +270,7 @@
GetRawNYTBestSellersListNames 以字符串形式返回的原始 JSON 结果实际上是图 12 中的 JSON 对象。这也可以通过检查 [API 控制台](http://developer.nytimes.com/io-docs)来检查。
![](../images/00015.jpeg)
![](img/00015.jpeg)
*图 12:NYT 畅销书排行榜名称返回的 JSON 对象。*
......
......@@ -981,7 +981,7 @@ BayesEngine 还支持加一平滑,默认情况下,它是假设和应用的(
运行这段代码会产生如图 13 所示的结果。
![](../images/00016.jpeg)
![](img/00016.jpeg)
*图 13:来自围绕样本数据集的贝叶斯引擎包装器的结果*
......@@ -1157,7 +1157,7 @@ IP 地址:^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][
的流行 NER 实现。NET 和 C#是(NER)的[斯坦福命名实体识别器](http://sergey-tihon.github.io/Stanford.NLP.NET/StanfordNER.html)。NET,也可以通过 NuGet 获得。
![](../images/00017.jpeg)
![](img/00017.jpeg)
*图 14:作为 NuGet 包安装的斯坦福 NER*
......@@ -1372,7 +1372,7 @@ IP 地址:^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][
在识别中,调用斯坦福 NER 的分类器. class ifythong 方法,并解析结果。这将产生如图 15 所示的输出。
![](../images/00018.jpeg)
![](img/00018.jpeg)
*图 15:斯坦福 NER C#实现输出*
......
......@@ -60,7 +60,7 @@ Delphi 可以为很多平台生产应用程序,然而它实际上是一个 Win
3. `Setup Languages`步骤允许您安装不同语言的产品(默认为英语)。
4. `Select Features`步骤允许您选择希望在产品中安装哪些功能(即库、帮助、示例、附加组件)。您可以选择一个功能并阅读树下方的简短描述。
![](../images/00003.jpeg)
![](img/00003.jpeg)
图 1:功能选择
......
......@@ -2,7 +2,7 @@
首先,我们将从头开始创建一个 Windows 桌面应用程序。为此,将鼠标指针移至主菜单,指向`[File|New]`项并选择`VCL Forms Application`
![](../images/00004.jpeg)
![](img/00004.jpeg)
图 2:德尔福主菜单
......@@ -16,7 +16,7 @@
如果您想更改刚刚添加的按钮上的文本,请左键单击您放置在表单内的控件,并指向`Object Inspector`窗口。在网格中滚动找到`Caption`属性并点击它输入新文本,用新标签替换默认值(按钮 1)。按回车键或点按“对象检查器”面板外的其他位置以完成更改。
![](../images/00005.jpeg)
![](img/00005.jpeg)
图 3:按钮属性
......@@ -24,7 +24,7 @@
如果要移动控件,可以按住鼠标左键单击并移动它,或者使用蓝点来更改它的大小。我们最终的主表单应该如下所示:
![](../images/00006.jpeg)
![](img/00006.jpeg)
图 4:“你好世界”表单
......@@ -45,23 +45,23 @@
要运行应用程序,选择`[Run|Run Without Debugging]`(或按 CTRL+F9),Delphi 将开始构建您的项目。
![](../images/00007.jpeg)
![](img/00007.jpeg)
图 5:构建进度窗口
如果在构建过程中没有检测到错误,Delphi 将启动应用程序可执行文件(该文件包含。. exe "扩展)。
| ![](../images/00008.gif) | 注意:Delphi 是一种本机编译语言,所以当您需要运行应用程序时,它总是会创建一个可执行文件,并查看它是否如您所期望的那样工作。你无法避免。您只能选择在不调试的情况下运行程序,这样一旦构建完成,Delphi 就不会附加到进程上,两个进程将继续独立运行。 |
| ![](img/00008.gif) | 注意:Delphi 是一种本机编译语言,所以当您需要运行应用程序时,它总是会创建一个可执行文件,并查看它是否如您所期望的那样工作。你无法避免。您只能选择在不调试的情况下运行程序,这样一旦构建完成,Delphi 就不会附加到进程上,两个进程将继续独立运行。 |
如果您正确执行了所有步骤,主窗体应该会出现,当您输入姓名并单击按钮时,您应该会看到以下消息。
![](../images/00009.jpeg)
![](img/00009.jpeg)
图 6:“你好,世界”跑步
通过调用 ShowMessage 函数调用的消息框是一个模态对话框。这意味着在您单击确定或右上角的关闭按钮关闭窗口之前,执行不会继续。
| ![](../images/00008.gif) | 注意:只要主窗体在屏幕上可见,应用程序就会一直运行。您可以最小化它或在桌面上移动它,当您关闭它时,应用程序会终止。 |
| ![](img/00008.gif) | 注意:只要主窗体在屏幕上可见,应用程序就会一直运行。您可以最小化它或在桌面上移动它,当您关闭它时,应用程序会终止。 |
恭喜你,你刚刚创建了你的第一个 Delphi 程序!
......@@ -73,7 +73,7 @@
部署程序不需要安装程序或打包工具:您可以简单地将可执行文件复制到目标机器并启动它。
| ![](../images/00010.jpeg) | 提示:有时您的应用程序可能需要使用外部文件、资源或数据库;在这些情况下,我建议使用安装工具来确保所有必需的文件都被复制到目标机器上。这让用户可以自定义过程,如程序目标目录、可选组件的选择、帮助手册和示例。如果你想要一个简单易用的工具,看看 [Inno Setup](http://www.jrsoftware.org) (顺便说一下,它是用 Delphi 构建的!) |
| ![](img/00010.jpeg) | 提示:有时您的应用程序可能需要使用外部文件、资源或数据库;在这些情况下,我建议使用安装工具来确保所有必需的文件都被复制到目标机器上。这让用户可以自定义过程,如程序目标目录、可选组件的选择、帮助手册和示例。如果你想要一个简单易用的工具,看看 [Inno Setup](http://www.jrsoftware.org) (顺便说一下,它是用 Delphi 构建的!) |
在这里,我们只触及了用德尔福可以实现的表面,我想你已经可以清楚地知道快速应用程序开发(RAD)方法在多大程度上加快了开发过程。
......
在我们深入研究您可以用 Delphi 构建的每一种项目之前,您应该接触一下 IDE 中可用的基本工具,看看它们在具体环境中是如何工作的。
![](../images/00011.jpeg)
![](img/00011.jpeg)
图 7:德尔福集成开发环境
工具选项板包含所有可用的组件和视觉控件。
![](../images/00012.jpeg)
![](img/00012.jpeg)
图 8:工具面板
......@@ -14,11 +14,11 @@
工具选项板中的所有项目都根据其使用上下文或所属的库进行分类。只需单击您想要查看其包含的组件和控件列表的类别名称。
| ![](../images/00010.jpeg) | 提示:工具面板支持“增量搜索”当面板处于焦点并且光标在搜索框内闪烁时,开始键入所需组件的名称。调色板仅显示与您输入的文本匹配的项目。 |
| ![](img/00010.jpeg) | 提示:工具面板支持“增量搜索”当面板处于焦点并且光标在搜索框内闪烁时,开始键入所需组件的名称。调色板仅显示与您输入的文本匹配的项目。 |
窗体设计器占据了集成开发环境窗口的主要区域,它是设计应用程序的大部分动作发生的地方。这里是您放置组件和视觉控件的地方,就像您刚才在“Hello World”示例中所做的那样。
![](../images/00013.jpeg)
![](img/00013.jpeg)
图 9:表单设计器
......@@ -28,11 +28,11 @@
现在尝试添加一个非可视组件:选择并拖动`TActionList`组件到您的表单中。任何组件在设计时都会在表单上显示为图标,但是在运行时您将永远看不到它,因为它只提供业务逻辑和行为,因此它没有可视化表示。
| ![](../images/00010.jpeg) | 提示:有时您的表单上会有很多组件,这可能会使您很难使用可视化控件,因为组件会不断妨碍您。出现这种情况时,您可以从主菜单中选择[编辑&#124;隐藏非可视组件],或者按 CTRL+H 从视图中隐藏它们。完成后,选择该菜单项或再次按快捷键来恢复它们。 |
| ![](img/00010.jpeg) | 提示:有时您的表单上会有很多组件,这可能会使您很难使用可视化控件,因为组件会不断妨碍您。出现这种情况时,您可以从主菜单中选择[编辑&#124;隐藏非可视组件],或者按 CTRL+H 从视图中隐藏它们。完成后,选择该菜单项或再次按快捷键来恢复它们。 |
如果点击表单设计器并从主菜单中选择`[View|Toggle Form/Unit]`(或按下`F12`),Delphi 会立即切换到代码编辑器窗口。
![](../images/00014.jpeg)
![](img/00014.jpeg)
图 10:代码编辑器
......@@ -42,11 +42,11 @@
如果按下`F12`键或选择`[View|Toggle Form/Unit]`,德尔福再次切换到表单设计器。
| ![](../images/00010.jpeg) | 提示:工具面板支持“增量搜索”这意味着当面板处于焦点并且光标在搜索框内闪烁时,调色板仅显示与您输入的文本匹配的项目。 |
| ![](img/00010.jpeg) | 提示:工具面板支持“增量搜索”这意味着当面板处于焦点并且光标在搜索框内闪烁时,调色板仅显示与您输入的文本匹配的项目。 |
“结构视图”显示一个树形图,该图会根据活动文档而变化。
![](../images/00015.jpeg)
![](img/00015.jpeg)
图 11:结构视图
......@@ -56,7 +56,7 @@
对象检查器是一个面板,您可以在其中编辑选定表单、组件和视觉控件的所有属性。
![](../images/00016.jpeg)
![](img/00016.jpeg)
图 12:对象检查器
......@@ -79,13 +79,13 @@
项目管理器显示属于项目或一组项目的文件,以分层形式显示它们,并包括一些“特殊节点”,这些节点指的是构建配置、目标平台和其他资源。
![](../images/00017.jpeg)
![](img/00017.jpeg)
图 13:项目经理
此面板有一个工具栏,可让您向项目中添加新文件、更改树中列出的项目的排列、更改应用程序的目标平台或快速激活不同的构建配置—只需指向工具栏即可查看每个按钮的功能。
| ![](../images/00008.gif) | 注意:如果您仔细查看项目树,您会注意到主窗体有两个链接在一起的文件。一个文件包含表单的定义,即您在对象检查器中为表单及其内部组件设置的值。另一个文件包含指导其行为的 Pascal 代码(可以使用代码编辑器查看和编辑的代码)。 |
| ![](img/00008.gif) | 注意:如果您仔细查看项目树,您会注意到主窗体有两个链接在一起的文件。一个文件包含表单的定义,即您在对象检查器中为表单及其内部组件设置的值。另一个文件包含指导其行为的 Pascal 代码(可以使用代码编辑器查看和编辑的代码)。 |
集成开发环境中还有许多其他面板可用。其中一些是隐藏的,除非有相关内容显示。其他人共享我们已经讨论过的面板的相同选项卡组。
......@@ -93,7 +93,7 @@
您可以添加新连接,也可以编辑和删除现有连接。
![](../images/00018.jpeg)
![](img/00018.jpeg)
图 14:数据浏览器
......@@ -101,7 +101,7 @@
“模型视图”面板允许您向项目添加对建模的支持。这将创建其内容的逻辑表示,其中项目是一个具有根命名空间的包,每个类型都是一个元素节点。
![](../images/00019.jpeg)
![](img/00019.jpeg)
图 15:模型视图
......@@ -109,7 +109,7 @@
“类资源管理器”面板显示项目中类的分层视图,类似于“结构视图”,但功能更强大。例如,您可以应用几个重构选项,向类(即方法、字段、属性)添加新成员,以及搜索符号的用法。
![](../images/00020.jpeg)
![](img/00020.jpeg)
图 16:类浏览器
......@@ -117,7 +117,7 @@
在这里,您可以看到在代码中使用// TODO 注释定义的任务列表,并手动将新的全局任务添加到您的项目中,保存在单独的”。todo”文件。
![](../images/00021.jpeg)
![](img/00021.jpeg)
图 17:待办事项列表
......@@ -127,19 +127,19 @@
你可以把它想象成一个虚拟桌面,所有最受欢迎的设备(桌面和移动设备)都在这里摆放,看看你的应用在上面会是什么样子。这包括每个设备的不同外形,因此您可以微调您的用户界面,并确保它在您选择的目标设备上有效工作。
![](../images/00022.jpeg)
![](img/00022.jpeg)
图 18:多设备预览
默认情况下,“消息”面板是隐藏的,但是当您构建项目后出现错误时,它会变得可见,并显示需要您注意的警告或提示。
![](../images/00023.jpeg)
![](img/00023.jpeg)
图 19:消息
默认情况下,该面板通常是隐藏的,当您对代码应用重构时,Delphi 会将其展开,显示所有预期更改的预览。然后,您可以选择继续应用它们或取消操作。
![](../images/00024.jpeg)
![](img/00024.jpeg)
图 20:重构预览
......
......@@ -4,7 +4,7 @@ Pascal 是一种强类型语言,它趋向于简单、优雅和干净。由于
Object Pascal 语言的 Delphi 化身功能强大,充满了许多特性,如匿名方法、泛型和运算符重载。如果你在学校使用过帕斯卡,现在的语言——尤其是在德尔菲——远不止如此。
| ![](../images/00008.gif) | 注意:请始终记住 Pascal 是一种不区分大小写的语言,就像 Visual Basic 一样。但是,我建议您保持用作标识符的名称的一致性。德尔福将通过代码洞察技术和提出在给定上下文中有效的标识符列表来帮助完成这项任务。这有助于您快速完成代码中的说明。 |
| ![](img/00008.gif) | 注意:请始终记住 Pascal 是一种不区分大小写的语言,就像 Visual Basic 一样。但是,我建议您保持用作标识符的名称的一致性。德尔福将通过代码洞察技术和提出在给定上下文中有效的标识符列表来帮助完成这项任务。这有助于您快速完成代码中的说明。 |
即使世界上的每一个编译器总是忽略它们或者把它们去掉,注释对于记录代码中最微妙的部分是必不可少的,也是任何编程书籍中解释的第一个元素。
......@@ -53,11 +53,11 @@ Delphi 中的每种类型的项目都有一个主程序文件,其基本组织
关键字 program 表示存在一个可运行的应用程序,但是您也可以看到类似 library(对于动态链接库)或 package(对于 Delphi 包,它是具有运行时和设计时支持的类和组件的捆绑包)的东西。
| ![](../images/00008.gif) | 注意:无论项目类型如何,您都可以从主菜单中选择[项目&#124;查看源代码]来打开主程序源文件。 |
| ![](img/00008.gif) | 注意:无论项目类型如何,您都可以从主菜单中选择[项目&#124;查看源代码]来打开主程序源文件。 |
begin…end 代码块包含程序启动时执行的指令,实际上是应用程序的入口点。
| ![](../images/00010.jpeg) | 提示:我不建议在主程序文件中放很多代码。您的逻辑应该使用最好的、最流行的编程实践来实现。这意味着创建单独的模块,其中每个模块都包含负责模型特定方面(例如数据存储、日志记录、安全性等)的数据类型和类。) |
| ![](img/00010.jpeg) | 提示:我不建议在主程序文件中放很多代码。您的逻辑应该使用最好的、最流行的编程实践来实现。这意味着创建单独的模块,其中每个模块都包含负责模型特定方面(例如数据存储、日志记录、安全性等)的数据类型和类。) |
那么,如何在 Delphi 中创建代码模块呢?
......@@ -78,7 +78,7 @@ begin…end 代码块包含程序启动时执行的指令,实际上是应用
unit 关键字后面是一个标识符,它作为该单元包含的所有元素的一种命名空间。
| ![](../images/00008.gif) | 注意:单位名称必须始终与文件名一致。例如,如果单元名为“我的模块”,它必须保存在一个名为“我的模块. pas”的文件中。如果您愿意,您可以始终使用虚线表示法,如“我的公司”。我的模块”,以创建一种命名空间。 |
| ![](img/00008.gif) | 注意:单位名称必须始终与文件名一致。例如,如果单元名为“我的模块”,它必须保存在一个名为“我的模块. pas”的文件中。如果您愿意,您可以始终使用虚线表示法,如“我的公司”。我的模块”,以创建一种命名空间。 |
虽然看起来很奇怪,但关键字 end 实际上结束了该单元。
......@@ -109,7 +109,7 @@ unit 关键字后面是一个标识符,它作为该单元包含的所有元素
此示例显示了一个静态链接例程,该例程将一个字符串作为参数,然后将该字符串连接到自身,并作为结果返回该值。
| ![](../images/00008.gif) | 注意:如果您对 AValue 参数声明之前的 const 关键字的含义感到好奇,它会阻止函数更改该变量的值。该约束允许编译器通过信任函数不改变值来应用优化,从而安全地传递一个指向字符串值的指针,而不创建整个字符串的副本。 |
| ![](img/00008.gif) | 注意:如果您对 AValue 参数声明之前的 const 关键字的含义感到好奇,它会阻止函数更改该变量的值。该约束允许编译器通过信任函数不改变值来应用优化,从而安全地传递一个指向字符串值的指针,而不创建整个字符串的副本。 |
如果例程有返回值,可以使用 function 关键字,否则必须使用 procedure。当您调用返回值的函数时,您可以自由选择是否使用返回值。
......@@ -117,7 +117,7 @@ unit 关键字后面是一个标识符,它作为该单元包含的所有元素
要使用已经在另一个单元中声明的元素,您必须在您的单元中添加一个 uses 子句,后跟一个逗号分隔的要导入的单元列表。
| ![](../images/00008.gif) | 注意:请始终记住,您只能导入在单元的接口部分中声明的内容;实现部分的所有内容都将保持隐藏。 |
| ![](img/00008.gif) | 注意:请始终记住,您只能导入在单元的接口部分中声明的内容;实现部分的所有内容都将保持隐藏。 |
查看代码清单 7,看看这是如何工作的。
......@@ -155,7 +155,7 @@ unit 关键字后面是一个标识符,它作为该单元包含的所有元素
局部变量必须在 begin…end 块之前声明,begin…end 块标记了过程和函数的主体部分,因此不能“内联”声明变量(例如,就像在 C#中一样)。
| ![](../images/00008.gif) | 注意:每个标识符必须是有效的,才能被德尔福编译器毫无怨言地接受。要做到这一点,千万不要用数字作为名字的开头,也不要用保留字作为单位、例程、类型和变量。 |
| ![](img/00008.gif) | 注意:每个标识符必须是有效的,才能被德尔福编译器毫无怨言地接受。要做到这一点,千万不要用数字作为名字的开头,也不要用保留字作为单位、例程、类型和变量。 |
在使用任何变量之前,必须使用:=运算符为其赋值。
......@@ -205,7 +205,7 @@ Delphi 使用整数类型存储非浮点值。我在表 2 中总结了它们。
Delphi 提供了一种布尔类型来表达布尔值和执行标准的布尔逻辑操作。
| ![](../images/00008.gif) | 注意:还有其他可用的布尔类型(字节布尔、单词布尔、长布尔),但它们是为了向后兼容而存在的。 |
| ![](img/00008.gif) | 注意:还有其他可用的布尔类型(字节布尔、单词布尔、长布尔),但它们是为了向后兼容而存在的。 |
代码清单 9:布尔类型
......@@ -252,7 +252,7 @@ Delphi 允许您将一个 AnsiString 赋值给一个执行隐式转换的 unicod
到目前为止,世界上的其他地方都(或者应该)符合 Unicode,所以如果您没有处理这些场景,并且不必提供一些向后兼容性,那么就坚持使用常见的 Char 和 String 类型。
| ![](../images/00008.gif) | 注意:请注意,Delphi 字符串索引在桌面和移动编译器上实际上是不同的:它是 1-基于桌面和 0-基于移动。如果您使用 RTL 字符串函数来访问字符串中的字符,请注意这一点,或者更好的是,使用 TStringHelper 静态类及其方法在安全模式下执行字符串操作。 |
| ![](img/00008.gif) | 注意:请注意,Delphi 字符串索引在桌面和移动编译器上实际上是不同的:它是 1-基于桌面和 0-基于移动。如果您使用 RTL 字符串函数来访问字符串中的字符,请注意这一点,或者更好的是,使用 TStringHelper 静态类及其方法在安全模式下执行字符串操作。 |
您还可以定义自己的限定范围的序数类型,指定下限和上限,如代码清单 12 所示。
......@@ -288,7 +288,7 @@ Delphi 允许您将一个 AnsiString 赋值给一个执行隐式转换的 unicod
| 货币 | -922337203685477.5808
..922337203685477.5807 | 10-20 | eight |
| ![](../images/00008.gif) | 注意:当用于涉及货币值的计算时,货币数据类型存储为 64 位整数,以最小化舍入误差。 |
| ![](img/00008.gif) | 注意:当用于涉及货币值的计算时,货币数据类型存储为 64 位整数,以最小化舍入误差。 |
您可以声明包含任何类型元素的数组,无论是静态的、具有固定长度和指定范围的,还是动态的、具有可变长度的,您可以在运行时通过调用 SetLength 函数来调整可变长度。
......@@ -311,7 +311,7 @@ Delphi 允许您将一个 AnsiString 赋值给一个执行隐式转换的 unicod
值的顺序并不重要,元素只能添加到同一个集合中一次。把它想象成一个文氏图的软件表示。
![](../images/00025.jpeg)
![](img/00025.jpeg)
图 21:维恩图
......@@ -355,7 +355,7 @@ Delphi 允许您将一个 AnsiString 赋值给一个执行隐式转换的 unicod
```
| ![](../images/00010.jpeg) | 提示:我建议在字段数量有限时使用记录类型,并且您可以保持结构尽可能简单。如果必须表示更复杂的对象的状态或在引用之间移动,记录会变得低效,您应该创建一个类来代替。 |
| ![](img/00010.jpeg) | 提示:我建议在字段数量有限时使用记录类型,并且您可以保持结构尽可能简单。如果必须表示更复杂的对象的状态或在引用之间移动,记录会变得低效,您应该创建一个类来代替。 |
像任何其他传统编程语言一样,Object Pascal 支持许多运算符,您可以使用这些运算符进行计算、连接和逻辑比较。
......@@ -399,7 +399,7 @@ Delphi 允许您将一个 AnsiString 赋值给一个执行隐式转换的 unicod
| 逻辑或 | 或者 | 甲还是乙 |
| 异或 | 异或运算 | 异或 |
| ![](../images/00008.gif) | 注意:Delphi 使用一种叫做布尔短路求值的东西来确定布尔表达式的结果值。计算在表达式中从左到右进行,一旦可以确定结果,计算就停止,并返回表达式值。您可以通过编译器指令{$B+}打开(这是默认的)或关闭(使用“完全布尔求值”代替)此模式。 |
| ![](img/00008.gif) | 注意:Delphi 使用一种叫做布尔短路求值的东西来确定布尔表达式的结果值。计算在表达式中从左到右进行,一旦可以确定结果,计算就停止,并返回表达式值。您可以通过编译器指令{$B+}打开(这是默认的)或关闭(使用“完全布尔求值”代替)此模式。 |
一些比较运算符和算术运算符可用于包含集值和元素的表达式中。
......@@ -424,7 +424,7 @@ Delphi 允许您将一个 AnsiString 赋值给一个执行隐式转换的 unicod
| 地址 | @ | @myvalue |
| 的价值 | ^ | ^MyPointer |
| ![](../images/00008.gif) | 注意:当您在 Delphi 中处理对象时,您实际上是在使用引用,它只不过是一个指针变量,包含您想要操作的对象的地址。为了保持代码清晰可读,可以省略^运算符来访问对象成员(属性、字段和方法)。 |
| ![](img/00008.gif) | 注意:当您在 Delphi 中处理对象时,您实际上是在使用引用,它只不过是一个指针变量,包含您想要操作的对象的地址。为了保持代码清晰可读,可以省略^运算符来访问对象成员(属性、字段和方法)。 |
还有一些操作符需要提及,在一些特殊场合会变得很方便。
......@@ -499,7 +499,7 @@ Delphi 允许您将一个 AnsiString 赋值给一个执行隐式转换的 unicod
| `case`选择`of`  1:`begin`*// TODO:此处说明...*`end`;  2:`begin`*// TODO:此处说明...*`end`;  3:`begin`*// TODO:此处说明...*`end``else begin`*// TODO:此处说明...*`end``end`; |
| ![](../images/00008.gif) | 注意:只有标量类型变量可以与 case 语句一起使用,并且任何 Case 都必须用常数值进行标记,因此不能使用对象引用或浮点值。如果你认为这是一个很大的限制,请注意使用许多案例陈述是不鼓励的,并且被认为是“反模式”。最好使用面向对象编程和虚拟方法来建模类似的业务逻辑。 |
| ![](img/00008.gif) | 注意:只有标量类型变量可以与 case 语句一起使用,并且任何 Case 都必须用常数值进行标记,因此不能使用对象引用或浮点值。如果你认为这是一个很大的限制,请注意使用许多案例陈述是不鼓励的,并且被认为是“反模式”。最好使用面向对象编程和虚拟方法来建模类似的业务逻辑。 |
对象 Pascal 提供了不同的语句来实现代码中不同类型的循环。
......
......@@ -79,7 +79,7 @@ Delphi 完全支持面向对象编程范式(OOP)。
像任何其他帕斯卡类型一样,类可以在一个单元的接口部分声明,以使它们对其他单元可用。若要在声明类的同一单元中使用该类,请将其放在实现部分。
| ![](../images/00010.jpeg) | 提示:您不必手动编写类实现的每一行。一旦在接口部分完成了类的声明,将编辑器光标放在里面,然后按 CTRL+SHIFT+C 自动创建它的实现。 |
| ![](img/00010.jpeg) | 提示:您不必手动编写类实现的每一行。一旦在接口部分完成了类的声明,将编辑器光标放在里面,然后按 CTRL+SHIFT+C 自动创建它的实现。 |
如果您没有指定祖先类,Delphi 会假设您正在扩展到 Object,它是所有类的母类,并为内存分配和泛型对象管理提供基本支持。
......@@ -156,7 +156,7 @@ TShape2D 类有一些字段:
这些字段在私有部分下声明,因此类外的客户端代码无法访问它们。这意味着更改它们的值必须通过调用像 SetHeight()和 SetWidth()这样的方法来完成,或者通过更改链接到字段的属性值来完成。
| ![](../images/00008.gif) | 注意:您在这里看到一些编码约定在起作用:所有类型通常以字母“T”开头,字段以字母“f”开头。如果您遵循这些广泛使用的约定,您将能够轻松阅读其他人编写的代码。 |
| ![](img/00008.gif) | 注意:您在这里看到一些编码约定在起作用:所有类型通常以字母“T”开头,字段以字母“f”开头。如果您遵循这些广泛使用的约定,您将能够轻松阅读其他人编写的代码。 |
创建此类的实例时,字段会自动初始化为默认值,因此任何整数字段都会变成 0(零),字符串为空,布尔值为假,等等。
......@@ -252,7 +252,7 @@ TShape2D 类有一些字段:
在上面的示例中,当您读取 Height 属性时,Delphi 从私有的 FH h8 字段中获取值,该字段在 read 子句之后指定;设置 Height 的值时,Delphi 调用 write 子句后指定的 SetHeight 方法,并传递新值。
| ![](../images/00008.gif) | 注意:读写访问器都是可选的:您可以创建只读和只写属性。 |
| ![](img/00008.gif) | 注意:读写访问器都是可选的:您可以创建只读和只写属性。 |
属性提供了字段的优点和易于访问的特性,但对客户端代码传入的值保留了一层保护。
......@@ -302,7 +302,7 @@ TShape2D 类有一些字段:
```
| ![](../images/00008.gif) | 注意:每个有抽象方法的类都应该标记为抽象本身。 |
| ![](img/00008.gif) | 注意:每个有抽象方法的类都应该标记为抽象本身。 |
抽象方法缺乏实现;它必须是一个虚拟方法,并且子类必须覆盖它。
......@@ -324,7 +324,7 @@ TShape2D 类有一些字段:
显然你不能在基类中调用继承的方法,因为它是抽象的,会导致`AbstractError`
| ![](../images/00010.jpeg) | 提示:当您创建抽象类的实例时,编译器会发出警告。您不应该这样做来避免遇到对抽象方法的调用。 |
| ![](img/00010.jpeg) | 提示:当您创建抽象类的实例时,编译器会发出警告。您不应该这样做来避免遇到对抽象方法的调用。 |
对象 Pascal 也支持静态成员。实例构造函数和析构函数本质上是静态的,但是您可以向类中添加静态字段、成员和属性。
......@@ -379,13 +379,13 @@ ProcessorCount 变量是一个私有的静态成员,由于类 var 关键字,
保存对所创建对象的引用的变量必须是同一类型或祖先类型。
| ![](../images/00010.jpeg) | 提示:如果要使引用类型变量指向无对象,可以将其设置为 nil,这是一个代表空引用值的关键字,就像 C#中的 null 一样。 |
| ![](img/00010.jpeg) | 提示:如果要使引用类型变量指向无对象,可以将其设置为 nil,这是一个代表空引用值的关键字,就像 C#中的 null 一样。 |
调用方法`LoadFromFile`引入`TMemoryStream`。这将缓冲指定文件中包含的所有数据(路径被指定为方法的第一个参数)。
然后调用`Free`方法销毁该对象。
| ![](../images/00008.gif) | 注意:为什么不调用 Destroy()方法而不是 Free()?毕竟,Destroy()是“官方”析构函数。您必须调用 Free(),因为它是非虚拟方法,并且具有静态地址,因此无论对象类型是什么,都可以安全地调用它。Free()方法还会在调用 Destroy()之前检查引用是否未赋值为零。 |
| ![](img/00008.gif) | 注意:为什么不调用 Destroy()方法而不是 Free()?毕竟,Destroy()是“官方”析构函数。您必须调用 Free(),因为它是非虚拟方法,并且具有静态地址,因此无论对象类型是什么,都可以安全地调用它。Free()方法还会在调用 Destroy()之前检查引用是否未赋值为零。 |
你可能会问自己“尝试……最终构造”代表什么。
......@@ -412,7 +412,7 @@ Delphi 没有垃圾收集器;您负责释放您创建的每个对象。try…f
如果`SomeObject`不是`TSomeClass`类型或者它的后代,你会得到一个例外。
| ![](../images/00008.gif) | 注意:不要同时使用 is 和作为运算符,因为它们执行相同的类型检查操作,这在计算方面有一定的成本。如果运算符检查成功,请始终使用直接转换。但是,在没有检查之前,不要做直接转换。 |
| ![](img/00008.gif) | 注意:不要同时使用 is 和作为运算符,因为它们执行相同的类型检查操作,这在计算方面有一定的成本。如果运算符检查成功,请始终使用直接转换。但是,在没有检查之前,不要做直接转换。 |
接口是您可以放在实现之间的较薄的层,并且是实现代码高度解耦的基本工具。
......@@ -430,7 +430,7 @@ Delphi 没有垃圾收集器;您负责释放您创建的每个对象。try…f
任何类只能有一个祖先,但是它可以实现任意数量的接口。
| ![](../images/00008.gif) | 注意:每个接口声明必须有一个关联的 GUID。这个需求是在 COM 支持下引入的,但是也有很多 RTL 的部分涉及到使用 GUIDs 的接口。这可能看起来很烦人,但是将 GUID 添加到界面上确实又快又简单:只需在代码编辑器中按 CTRL+SHIFT+G 组合键。 |
| ![](img/00008.gif) | 注意:每个接口声明必须有一个关联的 GUID。这个需求是在 COM 支持下引入的,但是也有很多 RTL 的部分涉及到使用 GUIDs 的接口。这可能看起来很烦人,但是将 GUID 添加到界面上确实又快又简单:只需在代码编辑器中按 CTRL+SHIFT+G 组合键。 |
而类有`TObject`作为共同的祖先,接口都有`IInterface`
......
......@@ -30,17 +30,17 @@ VCL 首字母缩略词的“V”代表“可视化”,不仅指可视化控件
。identcache | 这些文件由 Delphi 在开发过程中用来存储快速访问信息。如果你使用版本控制系统(我希望你真的在使用!)将这些文件添加到“忽略列表”中,并在将您的源代码解决方案分发给其他人之前将其删除(当 Delphi 未运行时)。 |
| 统计文件 | 。斯达 | 这个文件包含关于您的项目的统计信息,比如您花了多少秒编写代码、编译和调试。 |
| ![](../images/00008.gif) | 注意:请记住,您总是可以在“项目管理器”窗口中看到组成项目的主要文件。 |
| ![](img/00008.gif) | 注意:请记住,您总是可以在“项目管理器”窗口中看到组成项目的主要文件。 |
你可能想做的第一件事是给你的项目分配一个主标题。选择`[Project|Options]`(或按 Ctrl+Shift+F11)调出项目选项对话框。
![](../images/00026.jpeg)
![](img/00026.jpeg)
图 22:项目选项对话框
您可以使用左侧的导航树从对话框中选择一个页面,并更改项目的设置。选择`[Application|Appearance]`页面,影响应用程序的视觉外观。例如,我在“标题”框中键入了“DelphiPad”,这样标题就会显示在窗口任务栏、任务列表以及任何有您正在运行的应用程序证据的地方。
| ![](../images/00010.jpeg) | 提示:Delphi 完全支持主题。从“外观”页面中,您可以选择一个或多个要嵌入到项目中的主题。启动时可以选择其中一个作为默认主题,但是您可以在代码中切换到另一个加载的主题,或者从外部文件加载它。 |
| ![](img/00010.jpeg) | 提示:Delphi 完全支持主题。从“外观”页面中,您可以选择一个或多个要嵌入到项目中的主题。启动时可以选择其中一个作为默认主题,但是您可以在代码中切换到另一个加载的主题,或者从外部文件加载它。 |
但是,一旦提交,标题信息将何去何从?尝试查看项目文件(。dpr)源代码从主菜单中选择`[Project|View Source]`。你应该看到一个类似这样的片段:
......@@ -69,23 +69,23 @@ VCL 首字母缩略词的“V”代表“可视化”,不仅指可视化控件
在许多情况下,德尔福会修改代码以响应在集成开发环境中执行的操作。要再次更改应用程序标题,可以直接在项目源文件中编辑字符串,也可以使用“项目选项”对话框。
| ![](../images/00010.jpeg) | 提示:我建议您尽可能多地使用 IDE 中可用的工具,因为直接手工编辑代码更容易出错,即使您已经成为专家。 |
| ![](img/00010.jpeg) | 提示:我建议您尽可能多地使用 IDE 中可用的工具,因为直接手工编辑代码更容易出错,即使您已经成为专家。 |
主窗体对我们的应用程序有着核心作用,这适用于用 Delphi 创建的大多数项目。让我们看看有哪些选项可以定制它。
双击项目管理器中的“主表单”文件,打开主表单(如果不可见)。如果显示代码编辑器,按 F12(或选择`[View|Toggle Form/Unit]`)切换到表单设计器窗口。你应该已经很熟悉了。
![](../images/00027.jpeg)
![](img/00027.jpeg)
图 23:空的主窗体
首先,通过转到对象检查器并将“名称”属性设置为“主窗体”,更改窗体的名称,使其更有意义
| ![](../images/00008.gif) | 注意:如果您切换到代码编辑器,您会注意到德尔福已经为您重命名了表单类名,将其更改为天猫通知。这是另一种情况,在这种情况下,德尔福保持代码与您在集成开发环境中的更改同步。 |
| ![](img/00008.gif) | 注意:如果您切换到代码编辑器,您会注意到德尔福已经为您重命名了表单类名,将其更改为天猫通知。这是另一种情况,在这种情况下,德尔福保持代码与您在集成开发环境中的更改同步。 |
然后我们更改标题,用应用程序的名称替换默认文本(“Form1”或类似的东西)。单击表单空白区域,移动到“对象检查器”面板,单击“标题”,然后插入标题。
![](../images/00028.jpeg)
![](img/00028.jpeg)
图 24:表单属性
......@@ -128,21 +128,21 @@ VCL 首字母缩略词的“V”代表“可视化”,不仅指可视化控件
要向表单添加主菜单,请从工具选项板的“标准”页面中选择`TMainMenu`组件,并将其放置在表单上。
| ![](../images/00008.gif) | 注意:即使主菜单有可视化表示,TMainMenu 也不是一个控件,而是一个存储菜单配置的简单组件。 |
| ![](img/00008.gif) | 注意:即使主菜单有可视化表示,TMainMenu 也不是一个控件,而是一个存储菜单配置的简单组件。 |
要定义主菜单的内容,双击`TMainMenu`组件实例调出`Menu Designer`编辑器。
![](../images/00029.jpeg)
![](img/00029.jpeg)
图 25:菜单设计器
| ![](../images/00008.gif) | 注意:许多组件支持“组件编辑器”,这是一种特殊的向导,一旦组件和控件被添加到表单中,就可以通过双击它们来启动。它们有助于一次加快更多属性值的设置,自动生成子组件或使用更合适、更直观的界面执行高级定制。 |
| ![](img/00008.gif) | 注意:许多组件支持“组件编辑器”,这是一种特殊的向导,一旦组件和控件被添加到表单中,就可以通过双击它们来启动。它们有助于一次加快更多属性值的设置,自动生成子组件或使用更合适、更直观的界面执行高级定制。 |
单击`Menu Designer`中的空白项目,并使用对象检查器输入标题,然后您可以在其上方或下方添加更多项目。如果右键单击任何菜单项,将出现一个弹出菜单,将预定义菜单加载或保存为模板,或将任何项目转换为子菜单。
如果要创建菜单分隔符,请创建一个菜单项,并在“标题”属性中插入“-”。
| ![](../images/00010.jpeg) | 提示:顺便说一下,不要将 TMainMenu 和 TPopupMenu 组件混淆:两者都使用 Menu Designer 来定义菜单项,但是虽然 TMainMenu 在表单的上侧创建了一个菜单,但是 TPopupMenu 可以“附加”到一些可视控件和组件上,以便在用户右键单击目标时出现。 |
| ![](img/00010.jpeg) | 提示:顺便说一下,不要将 TMainMenu 和 TPopupMenu 组件混淆:两者都使用 Menu Designer 来定义菜单项,但是虽然 TMainMenu 在表单的上侧创建了一个菜单,但是 TPopupMenu 可以“附加”到一些可视控件和组件上,以便在用户右键单击目标时出现。 |
您总是可以通过按下 Shift+Ctrl+F9 或选择`[Run|Run Without Debugging]`来启动程序,以查看主菜单是否真的如预期的那样工作。
......@@ -166,11 +166,11 @@ VCL 首字母缩略词的“V”代表“可视化”,不仅指可视化控件
双击组件,调出`Action List`编辑器。
| ![](../images/00008.gif) | 注意:动作列表组件编辑器实际上是一个标准的集合编辑器。它看起来很相似,对于每个具有从 TCollection 类型继承的属性的组件来说,它的工作方式是相同的。 |
| ![](img/00008.gif) | 注意:动作列表组件编辑器实际上是一个标准的集合编辑器。它看起来很相似,对于每个具有从 TCollection 类型继承的属性的组件来说,它的工作方式是相同的。 |
点击`New Action`按钮(或按下插入按钮)向`Actions (VCL)`列表框添加新动作。每个动作都是`TAction`类型的一个实例,代表一个可执行的命令。
![](../images/00030.jpeg)
![](img/00030.jpeg)
图 26:动作列表编辑器
......@@ -213,7 +213,7 @@ VCL 首字母缩略词的“V”代表“可视化”,不仅指可视化控件
事件处理程序是`TMainForm`的一种方法,作为`FileExitAction``OnExecute`事件的参考。
| ![](../images/00008.gif) | 注:VCL 表格申请通常在主表格关闭时终止。您应该总是求助于这种做法,避免任何其他残忍或异常的方式来关闭您的应用程序。 |
| ![](img/00008.gif) | 注:VCL 表格申请通常在主表格关闭时终止。您应该总是求助于这种做法,避免任何其他残忍或异常的方式来关闭您的应用程序。 |
我们如何将`FileExitAction`链接到点击时必须执行的视觉控件?
......@@ -231,7 +231,7 @@ VCL 首字母缩略词的“V”代表“可视化”,不仅指可视化控件
双击组件调出`ImageList Editor`。此对话框允许您从文件系统中选择图像,并将其分配给动作、菜单、工具栏、按钮和其他视觉控件。
![](../images/00031.jpeg)
![](img/00031.jpeg)
图 27:图像列表编辑器
......@@ -259,23 +259,23 @@ VCL 首字母缩略词的“V”代表“可视化”,不仅指可视化控件
选择`New Standard Action`,Delphi 将显示一个对话框,选择一个或多个您可以添加到项目中的内置动作。
![](../images/00032.jpeg)
![](img/00032.jpeg)
图 28:标准动作类
使用 Ctrl+Click 从可用的动作类列表中选择更多动作,然后按 OK 将它们添加到动作列表组件中。现在,它们将成为项目的一部分,并可分配给菜单项和工具按钮。
| ![](../images/00010.jpeg) | 提示:如果您将图像列表链接到操作列表,当您添加标准操作时,默认图标会自动插入到图像列表中。您可以随时用自己选择的图像替换新图标。 |
| ![](img/00010.jpeg) | 提示:如果您将图像列表链接到操作列表,当您添加标准操作时,默认图标会自动插入到图像列表中。您可以随时用自己选择的图像替换新图标。 |
标准操作附带一组分配给其属性的默认值,并且它们比默认操作具有更多的属性和事件,以自定义其行为并满足您的特定业务需求。
![](../images/00033.jpeg)
![](img/00033.jpeg)
图 29:标准动作属性
下面是运行完成的应用程序的截图。
![](../images/00034.jpeg)
![](img/00034.jpeg)
图 30: DelphiPad 示例运行
......
......@@ -30,7 +30,7 @@ FMX 还为窗口、菜单、对话框、控件、定时器和传感器等功能
* *选项卡式*,用选项卡和手势支持在它们之间切换来启动一个 2D 项目。
* *选项卡式导航*,类似于上一个模板,但增加了导航按钮,可以移动到下一个和上一个选项卡。
![](../images/00035.jpeg)
![](img/00035.jpeg)
图 31:多设备应用程序模板
......@@ -44,7 +44,7 @@ FMX 还为窗口、菜单、对话框、控件、定时器和传感器等功能
将控件添加到表单后,将`Align`属性设置为`Client`,使控件填满可用的客户端空间。然后启用`SearchVisible`属性,一个小搜索框将出现在列表控件的顶部。这将允许用户输入一个字符串来过滤列表视图中显示的内容。
| ![](../images/00010.jpeg) | 提示:TListView 支持“拉取刷新”功能。要启用它,请将 PullToRefresh 属性设置为 True,并处理 OnPullRefresh 事件以实现您对手势的自定义响应(即重新加载更新的项目)。 |
| ![](img/00010.jpeg) | 提示:TListView 支持“拉取刷新”功能。要启用它,请将 PullToRefresh 属性设置为 True,并处理 OnPullRefresh 事件以实现您对手势的自定义响应(即重新加载更新的项目)。 |
现在我们已经建立了项目列表,我们应该将它绑定到一个数据源,以查看控件内部的一些东西,并测试应用程序是如何工作的。
......@@ -54,7 +54,7 @@ FMX 还为窗口、菜单、对话框、控件、定时器和传感器等功能
现在是时候添加一些字段了。右键单击组件,选择`Add Field…`在组件产生的虚拟数据表中插入一个新字段。将弹出`Add Field`对话框。
![](../images/00036.jpeg)
![](img/00036.jpeg)
图 32:原型绑定源新字段
......@@ -66,19 +66,19 @@ LiveBindings 是一项适用于 VCL 和火猴框架的技术。
它基于将对象属性链接在一起的绑定表达式。例如,您可以将切换开关按钮的`Checked`属性绑定到任何视觉控件的`Enabled`属性,以根据按钮的状态启用或禁用它,而无需编写一行代码。
| ![](../images/00008.gif) | 注意:VCL 包括一个特殊的标准视觉控件家族,称为“数据控件”,支持数据绑定,而火猴依赖于 LiveBindings。 |
| ![](img/00008.gif) | 注意:VCL 包括一个特殊的标准视觉控件家族,称为“数据控件”,支持数据绑定,而火猴依赖于 LiveBindings。 |
您可以通过工具面板的 LiveBindings 页面中提供的一组组件来访问 LiveBindings 功能,但是更好的方法是通过`LiveBindings Designer`来访问它们。您可以从主菜单中选择`[View|LiveBindings Designer]`项来打开它。
`Title`字段从`TPrototypeBindingSource`元素拖动到`TListView`元素的`Item.Text`字段。
![](../images/00037.jpeg)
![](img/00037.jpeg)
图 33:实时绑定设计器
这将告诉控件使用每个原型记录的`Title`属性值作为每个列表项的文本。您应该已经在表单设计器和多设备预览面板中看到了预览。
![](../images/00038.jpeg)
![](img/00038.jpeg)
图 34:多设备预览
......@@ -105,7 +105,7 @@ LiveBindings 是一项适用于 VCL 和火猴框架的技术。
第一步是创建到数据库的连接。从工具选项板的`FireDAC`页面选择`TFDConnection`组件,然后双击该组件打开`FireDAC Connection Editor`
![](../images/00039.jpeg)
![](img/00039.jpeg)
图 35:防火墙连接编辑器
......@@ -130,7 +130,7 @@ LiveBindings 是一项适用于 VCL 和火猴框架的技术。
实时绑定设计器可以再次提供帮助。点击`LiveBindings Wizard`按钮,打开一个特殊的向导,通过一步一步的引导过程创建所需绑定组件的实例。
![](../images/00040.jpeg)
![](img/00040.jpeg)
图 36:实时绑定向导
......@@ -154,7 +154,7 @@ LiveBindings 是一项适用于 VCL 和火猴框架的技术。
4. 选择`Title`作为字段名,再次点击`Next`
5. 点击`Finish`结束向导。
| ![](../images/00008.gif) | 注意:向导可能会要求您删除现有链接。这是因为我们的 TListView 已经链接到了 TPrototypeBindSource 组件,并且只允许一个链接将控件绑定到任何数据源。 |
| ![](img/00008.gif) | 注意:向导可能会要求您删除现有链接。这是因为我们的 TListView 已经链接到了 TPrototypeBindSource 组件,并且只允许一个链接将控件绑定到任何数据源。 |
为了完成我们的示例应用程序,我们应该添加一些命令按钮,以允许用户向列表中添加新项目或删除现有项目。
......@@ -172,7 +172,7 @@ LiveBindings 是一项适用于 VCL 和火猴框架的技术。
*`Name`属性设置为`DeleteButton`
*`StyleLookup`属性设置为`deleteitembutton`
| ![](../images/00010.jpeg) | 提示:FireMonkey 附带了一组预定义的样式,您可以通过 StyleLookup 属性将其分配给控件。 |
| ![](img/00010.jpeg) | 提示:FireMonkey 附带了一组预定义的样式,您可以通过 StyleLookup 属性将其分配给控件。 |
最后,在工具栏中添加一个`TLabel`控件作为标题栏,并设置:
......@@ -308,7 +308,7 @@ LiveBindings 是一项适用于 VCL 和火猴框架的技术。
如果你做的都很好,你应该会在项目管理器窗口中看到你的安卓设备,在安卓目标平台的目标文件夹下。
![](../images/00041.jpeg)
![](img/00041.jpeg)
图 37:安卓目标平台
......
......@@ -8,7 +8,7 @@
Windows 管理员可以通过几种方式管理服务。其中包括 Sc.exe 命令行工具、Windows Power Shell 和服务管理单元,该管理单元位于 Windows 控制面板的管理工具下,如下图所示。
![](../Images/image001.jpg)
![](img/image001.jpg)
图 1:服务管理单元
......@@ -26,10 +26,10 @@ Windows 管理员可以通过几种方式管理服务。其中包括 Sc.exe 命
* 检查服务依赖关系。
* 将服务列表导出到文本文件。
| ![](../Images/note.png) | 注意:自动(延迟)选项是在 Windows Vista 中引入的,旨在减少从启动到桌面的时间。但是,并非所有服务都支持延迟启动。 |
| ![](img/note.png) | 注意:自动(延迟)选项是在 Windows Vista 中引入的,旨在减少从启动到桌面的时间。但是,并非所有服务都支持延迟启动。 |
### 开发 Windows 服务
可以使用 Visual Studio 开发 Windows 服务。为了成为一个窗口服务,一个程序需要使用提供的模板以特定的方式进行编码。也就是说,它必须处理来自服务控制管理器的启动、停止和暂停消息,服务控制管理器是负责启动和停止服务的 Windows 组件。
| ![](../Images/tip.png) | 提示:服务必须在 Visual Studio 下的 Windows 服务应用程序项目中创建。 |
\ No newline at end of file
| ![](img/tip.png) | 提示:服务必须在 Visual Studio 下的 Windows 服务应用程序项目中创建。 |
\ No newline at end of file
......@@ -6,7 +6,7 @@
在加载 Microsoft Visual Studio 后,开发 Windows 服务的第一步是创建一个 Windows 服务类型项目。下图显示了 Visual Studio 中的这种项目对话框。
![new project window](../Images/image004.jpg)
![new project window](img/image004.jpg)
图 Visual Studio 窗口服务项目类型
......@@ -18,11 +18,11 @@
可以定制项目基线,以适应开发人员自己的需求。要这样做,必须重命名**解决方案****项目****Program.cs****Service1.cs** 节点。就本书而言,视窗服务项目将被命名为**监控服务。**下图为定制前后的 Visual Studio 解决方案资源管理器。
![](../Images/image005.jpg)
![](img/image005.jpg)
图 3:项目定制前后的解决方案资源管理器
| ![](../Images/tip.png) | 提示:您可以通过右键单击所需节点并从上下文弹出式菜单中应用“重命名”命令来快速重命名解决方案资源管理器树中的元素。重命名操作后,将触发 Visual Studio 重构代码工具。 |
| ![](img/tip.png) | 提示:您可以通过右键单击所需节点并从上下文弹出式菜单中应用“重命名”命令来快速重命名解决方案资源管理器树中的元素。重命名操作后,将触发 Visual Studio 重构代码工具。 |
## 应用入口点
......
......@@ -22,7 +22,7 @@ Windows 操作系统按类型对事件进行分类。例如,*信息事件*描
可以通过窗口事件查看器查看窗口事件日志中的条目。这可用于调试服务代码。事实上,这是唯一的方法,因为窗口服务没有用户界面。下图显示了窗口事件查看器的主窗口。
![](../Images/image006.jpg)
![](img/image006.jpg)
图 4:窗口事件查看器
......@@ -157,7 +157,7 @@ LogEvent 方法
请注意,`OnStart``OnStop`方法会写入 Windows 事件日志,通知它们各自被触发的日期和时间。在这种情况下,它被认为是两者的信息日志条目类型。
| ![](../Images/tip.png) | 提示:每次程序写入 Windows 事件日志时,都应该使用日志条目类型,以便阐明程序写入的原因。 |
| ![](img/tip.png) | 提示:每次程序写入 Windows 事件日志时,都应该使用日志条目类型,以便阐明程序写入的原因。 |
## 章节总结
......
......@@ -6,7 +6,7 @@
使用服务应用程序允许您自动向项目添加适当的安装程序。您也可以通过双击解决方案资源管理器树中的 **monitorservice.cs** 文件名(存储服务基类的位置)来完成此操作,将出现服务设计器屏幕(如下图所示)。
![](../Images/image007.jpg)
![](img/image007.jpg)
图 5:服务设计器屏幕
......@@ -59,17 +59,17 @@
* 服务名称–指示系统用来标识此服务的名称
* 开始类型–指示此服务的开始方式和时间(如简介中所述)
![](../Images/image008.jpg)
![](img/image008.jpg)
图 6:带有图标按钮的 ProjectInstaller.cs 设计器屏幕
单击每个按钮可以更改前面列出的属性。为了完成这个任务,需要使用对应于每个按钮的属性窗口,并在相应文本框的属性窗口中输入适当的值。
![](../Images/image009.jpg)
![](img/image009.jpg)
图 7:服务流程安装程序属性窗口
![](../Images/image010.jpg)
![](img/image010.jpg)
图 8:服务安装程序属性窗口
......
......@@ -240,7 +240,7 @@ XML 的根节点被称为`Parameters`,用于保存服务需要执行的任何
为了便于项目维护,文件备份过程将在一个单独的类定义中编码。为此,需要将类类型项添加到项目中。右键单击解决方案资源管理器树中的项目名称节点,然后单击**添加**子菜单的**类**项。这将弹出添加新项目对话框。
![](../Images/image011.jpg)
![](img/image011.jpg)
图 9:添加新项目对话框
......@@ -266,7 +266,7 @@ XML 的根节点被称为`Parameters`,用于保存服务需要执行的任何
整个类将被写在添加到项目中的基线代码上。必须考虑的一点是,前面提到的要求要求备份必须存储在 ZIP 文件中。**离子。Zip** 库将用于此目的,可在此下载[。下载后,需要将库文件复制到项目文件夹中,并添加到项目引用节点中。](http://dotnetzip.codeplex.com/releases/view/68268)
![](../Images/image012.jpg)
![](img/image012.jpg)
图 10:离子键。已将压缩库添加到引用节点
......
......@@ -82,7 +82,7 @@ Installutil.exe 使用反射检查指定的程序集,并查找所有将`System
两个文件看起来几乎一样。唯一的区别是卸载文件中用于 INSTALLUITL.EXE 的/U 选项。
| ![](../Images/note.png) | 注意:为了确保正确部署服务,Installutil.exe 必须包含在服务分发包中。 |
| ![](img/note.png) | 注意:为了确保正确部署服务,Installutil.exe 必须包含在服务分发包中。 |
## 服务分发包
......@@ -95,7 +95,7 @@ Installutil.exe 使用反射检查指定的程序集,并查找所有将`System
* BAT 安装文件
* BAT 卸载文件
| ![](../Images/tip.png) | 提示:为了便于分发,包可以以 zip 文件的形式发送,该文件可以在安装时在目标计算机中解压缩。 |
| ![](img/tip.png) | 提示:为了便于分发,包可以以 zip 文件的形式发送,该文件可以在安装时在目标计算机中解压缩。 |
## 章节总结
......
......@@ -10,13 +10,13 @@
第一步是使用 Visual Studio 创建一个名为 **MonitorServiceGUI** 的 Windows 窗体项目。要使用的编程语言将是 C#并且目标框架将是。NET 4。
![](../Images/image015.jpg)
![](img/image015.jpg)
图 11: MonitorServiceGUI 项目对话框
当 Visual Studio 结束项目创建时,在解决方案资源管理器的树中可以找到两个名为 **Form1.cs****Program.cs** 的文件。为清楚起见,这些文件将分别重命名为 **mainform.cs****mainprogram.cs** 。现在,解决方案资源管理器将如下图所示。
![](../Images/image016.jpg)
![](img/image016.jpg)
图 MonitorServiceGUI 项目的解决方案资源管理器
......@@ -75,7 +75,7 @@
当所有图形元素都放置在表单中,并且设置了前面提到的属性值时,`mainform`的设计器视图将如下图所示。
![](../Images/image017.jpg)
![](img/image017.jpg)
图 13:主窗体. cs 设计器视图
......@@ -402,7 +402,7 @@
创建`ServiceController`实例后,必须在其中设置两个属性来标识与之交互的服务:计算机名和您想要控制的服务名。
| ![](../Images/note.png) | 注意:默认情况下,MachineName 设置为本地计算机,因此您不需要更改它,除非您想将实例设置为指向另一台计算机。 |
| ![](img/note.png) | 注意:默认情况下,MachineName 设置为本地计算机,因此您不需要更改它,除非您想将实例设置为指向另一台计算机。 |
### 增加一个*系统。服务流程*参考
......@@ -410,7 +410,7 @@
要添加对程序集的引用,请在 Visual Studio 中右键单击项目名称并选择**添加引用**,然后浏览需要添加到应用程序中的程序集。
**![ServiceControllerImg1.jpg](../Images/image019.jpg)**
**![ServiceControllerImg1.jpg](img/image019.jpg)**
图 14:使用系统添加引用对话框。ServiceProcess 程序集
......@@ -647,15 +647,15 @@
现在项目已经准备好构建可执行文件了。对于一个人们只是在阅读而不一定在代码编辑器中跟随的环境,下面的截图显示了运行完成的程序。
![](../Images/image020.png)
![](img/image020.png)
图 15:充满数据的备份服务界面主窗体
![](../Images/image021.png)
![](img/image021.png)
图 16:备份服务界面主窗体,显示所有周和周末的列表
![](../Images/image022.png)
![](img/image022.png)
图 17:输入了错误源路径的备份服务接口
......
......@@ -26,7 +26,7 @@ ADSI 的事件跟踪是 ADSI LDAP 提供商的某些领域中增加的一项功
图 1 显示了 ADSI 架构的组件式图表。如我们所见,在顶层我们有一个系统对象,它包含当前安装的所有 ADSI 提供商的列表。列表中的每个条目又是特定于特定 ADSI 提供程序的命名空间对象。在每个名称空间容器对象的顶层根节点,我们已经定义了目录服务使用的任何目录系统对象。ADSI 提供了一组预定义的对象和接口,以便客户端应用程序可以使用一组统一的方法与目录服务进行交互。但是,ADSI 可能不提供目录服务的所有功能。在每个提供程序命名空间对象中找到的根节点容器对象包括一个 ADSI 模式容器对象。此对象包含该提供程序所有功能的定义。
![](../Images/image001.png)
![](img/image001.png)
图 1:显示名称空间容器对象的图表
......
......@@ -16,10 +16,10 @@
* **林、树和域:**林、树和域是活动目录网络中的逻辑分区。在部署中,对象被分组到域中。域被定义为共享同一 active directory 数据库的网络对象(计算机、用户、设备)的逻辑组。树是一个连续命名空间中的一个或多个域和域树的集合。建筑的顶部是森林。森林是共享共同配置的树的集合。林代表用户、计算机、组和其他对象可访问的安全边界。
* **组织单位:**域内持有的对象可以分组为组织单位。ou 可以为域提供层次结构。OU 是应用组策略(正式命名为组策略对象(GPO))的推荐级别。
| ![](../Images/note.png) | 注意:微软建议在结构上使用 ou 而不是域,以简化策略的实施和管理。 |
| ![](img/note.png) | 注意:微软建议在结构上使用 ou 而不是域,以简化策略的实施和管理。 |
这些对象之间有层次分类。下图提供了这种分类的可视化表示。
![](../Images/image003.png)
![](img/image003.png)
图 2:显示广告对象层次结构的图表
\ No newline at end of file
......@@ -10,4 +10,4 @@
我们将对托管在微软 Azure 上的 Windows Server 2012 R2 数据中心版虚拟机执行广告询问。当然,该虚拟机已经配置了活动目录和域名系统角色。虚拟机将位于西欧。
| ![](../Images/image004.png) | 提示:为了让您自己测试代码示例,重要的是您的域控制器和开发机器在同一个局域网中,否则它们将无法相互通信(除非您向外部用户公开 AD 服务)。然而,将广告服务暴露在互联网上是应该避免的。如果您在 Azure 中托管虚拟机,您可以通过使用相同的云服务将它们设置在同一局域网中。 |
\ No newline at end of file
| ![](img/image004.png) | 提示:为了让您自己测试代码示例,重要的是您的域控制器和开发机器在同一个局域网中,否则它们将无法相互通信(除非您向外部用户公开 AD 服务)。然而,将广告服务暴露在互联网上是应该避免的。如果您在 Azure 中托管虚拟机,您可以通过使用相同的云服务将它们设置在同一局域网中。 |
\ No newline at end of file
......@@ -16,7 +16,7 @@ SearchRoot 属性使您能够设置其他属性来执行以下任务:
* 执行分页搜索。设置**页面大小**属性,指定页面搜索中返回的最大对象数。如果不想执行分页搜索,请将 PageSize 属性设置为零(这是默认设置)。
* 使用**大小限制**属性指定要返回的最大条目数。如果将**大小限制**属性设置为默认值零,则服务器确定的默认值为 1000 个条目。
| ![](../Images/note.png) | 注意:如果返回条目的最大数量和时间限制超过了服务器上设置的限制,服务器设置将覆盖组件设置。 |
| ![](img/note.png) | 注意:如果返回条目的最大数量和时间限制超过了服务器上设置的限制,服务器设置将覆盖组件设置。 |
### find one 方法
......@@ -58,8 +58,8 @@ FindAll 方法执行搜索并返回找到的条目的集合。然而,由于我
此方法将对目录条目所做的更改保存到基础目录存储中。
| ![](../Images/note.png) | 注意:如果在调用 CommitChanges 之前调用 RefreshCache,对属性缓存的任何未提交的更改都将丢失。 |
| ![](../Images/tip.png) | 提示:使用目录服务会话类来读取、写入、删除、更改和添加到活动目录域服务层次结构。关联枚举:目录服务授权访问。 |
| ![](img/note.png) | 注意:如果在调用 CommitChanges 之前调用 RefreshCache,对属性缓存的任何未提交的更改都将丢失。 |
| ![](img/tip.png) | 提示:使用目录服务会话类来读取、写入、删除、更改和添加到活动目录域服务层次结构。关联枚举:目录服务授权访问。 |
### 复制法
......@@ -87,7 +87,7 @@ CopyTo 方法的第一个重载只接受另一个 DirectoryEntry 作为参数,
重命名方法更改此目录对象的名称。
| ![](../Images/note.png) | 注意:这也将影响用于引用该条目的路径。 |
| ![](img/note.png) | 注意:这也将影响用于引用该条目的路径。 |
## 搜索结果类
......@@ -105,13 +105,13 @@ CopyTo 方法的第一个重载只接受另一个 DirectoryEntry 作为参数,
从活动目录域服务层次结构中检索与搜索结果对应的目录尝试。
| ![](../Images/note.png) | 注意:对通过目录服务返回的每个搜索结果调用 GetDirectoryEntry 可能会很慢。 |
| ![](img/note.png) | 注意:对通过目录服务返回的每个搜索结果调用 GetDirectoryEntry 可能会很慢。 |
## 搜索结果集合类
**搜索结果集合**类包含活动目录层次结构在目录搜索查询期间返回的**搜索结果**实例。因为它继承自集合,所以它具有 Count 和 Item 属性,这两个属性分别表示集合中包含的项数和集合中的项本身(给定有效的索引)。即使在 SearchResultCollection 内部,使用属性加载字段,我们也可以获得在 directory archer . properties to load 中显式指定的属性列表。
| ![](../Images/note.png) | 注意:由于实现限制,当垃圾收集时,SearchResultCollection 类无法释放其所有非托管资源。为了防止内存泄漏,当不再需要 SearchResultCollection 对象时,必须调用 Dispose 方法。 |
| ![](img/note.png) | 注意:由于实现限制,当垃圾收集时,SearchResultCollection 类无法释放其所有非托管资源。为了防止内存泄漏,当不再需要 SearchResultCollection 对象时,必须调用 Dispose 方法。 |
## 用户主体类
......@@ -139,7 +139,7 @@ CopyTo 方法的第一个重载只接受另一个 DirectoryEntry 作为参数,
除了这些静态方法之外,UserPrincipal 类使我们更容易对用户对象执行操作,例如更改/终止他们的密码、检查组成员资格以及列出组成员资格。此外,UserPrincipal 类以更面向对象的方式封装了用户对象的属性 getter。
| ![](../Images/note.png) | 注:关于 UserPrincipal 类的更多信息可以在官方 MSDN 页面的[中找到。](http://msdn.microsoft.com/en-us/library/system.directoryservices.accountmanagement.userprincipal(v=vs.110).aspx) |
| ![](img/note.png) | 注:关于 UserPrincipal 类的更多信息可以在官方 MSDN 页面的[中找到。](http://msdn.microsoft.com/en-us/library/system.directoryservices.accountmanagement.userprincipal(v=vs.110).aspx) |
## principal context 类
......@@ -163,7 +163,7 @@ CopyTo 方法的第一个重载只接受另一个 DirectoryEntry 作为参数,
由于 AD 允许我们管理组和计算机,系统。AccountManagement 命名空间提供了两个从名为 Principal 的抽象类继承的类来管理它们。这些类是 GroupPrincipal 和 ComputerPrincipal,正如我们在 UserPrincipal 类中看到的,它们的主要功能是以一种更简单、更面向对象的方式包装 AD/DirectoryEntry 对象。在这种情况下,它可以是一个组或一台计算机。根据类引用的对象类型,您可以公开不同的方法和属性。注意到 GroupPrincipal 和 ComputerPrincipal 都继承自与 UserPrincipal 相同的基类这一事实,我们将不继续描述它们的公共(和静态)方法,因为它们是相似的,并且在某些情况下与本章前面讨论的方法相同。
| ![](../Images/note.png) | 注意:如果您想详细了解 GroupPrincipal 和 ComputerPrincipal 类,请参考以下链接:group principal:[http://msdn . Microsoft . com/en-us/library/system . director yservices . account management . group principal(v = vs . 110)。aspx](http://msdn.microsoft.com/en-us/library/system.directoryservices.accountmanagement.groupprincipal(v=vs.110).aspx)computer principal:[http://msdn . Microsoft . com/en-us/library/system . director yservices . account management . computer principal(v = vs . 110)。aspx](http://msdn.microsoft.com/en-us/library/system.directoryservices.accountmanagement.computerprincipal(v=vs.110).aspx) |
| ![](img/note.png) | 注意:如果您想详细了解 GroupPrincipal 和 ComputerPrincipal 类,请参考以下链接:group principal:[http://msdn . Microsoft . com/en-us/library/system . director yservices . account management . group principal(v = vs . 110)。aspx](http://msdn.microsoft.com/en-us/library/system.directoryservices.accountmanagement.groupprincipal(v=vs.110).aspx)computer principal:[http://msdn . Microsoft . com/en-us/library/system . director yservices . account management . computer principal(v = vs . 110)。aspx](http://msdn.microsoft.com/en-us/library/system.directoryservices.accountmanagement.computerprincipal(v=vs.110).aspx) |
## 匹配类型枚举
......
......@@ -17,7 +17,7 @@
```
![](../Images/image007.png)
![](img/image007.png)
图 3:连接成功,我们输出根中的子列表
......@@ -53,15 +53,15 @@
前面的代码非常简单。首先我们创建一个直接指向我们的用户 OU 的目录尝试对象,然后我们实例化一个目录射手对象,指定我们只想要返回用户类型的对象(如果变量 Users 设置为 true)。然后我们调用方法 FindAll(),由于它返回的集合,通过 for 循环,我们可以迭代结果。下面是程序输出的截图;在我们的例子中,我们在屏幕上打印用户显示名称和描述。
| ![](../Images/note.png) | 注意:为了显示 CN 和描述,必须将这些属性添加到。属性加载集合,如图所示。 |
| ![](img/note.png) | 注意:为了显示 CN 和描述,必须将这些属性添加到。属性加载集合,如图所示。 |
![](../Images/image009.png)
![](img/image009.png)
图 4:我们广告中的用户列表
正如我们通过布尔参数**用户**所看到的,我们可以很容易地改变查询的类型,以便获得包含在我们的用户组织单元中的组列表。
![](../Images/image010.png)
![](img/image010.png)
图 5:我们广告中的群组列表
......@@ -82,7 +82,7 @@
上面的代码片段几乎与我们之前讨论的代码片段相同,当时我们只需要列出广告实体,但是这次我们设法获得了单个广告实体的引用,更准确地说是一个用户。一旦我们做到了这一点,编辑它的属性就很简单了,我们只需要在 Property 集合中使用正确的索引(在这种情况下,索引由文字字符串表示)并设置我们想要的值。
| ![](../Images/tip.png) | 提示:记得在应用任何一致有效的修改后提交您的更改。 |
| ![](img/tip.png) | 提示:记得在应用任何一致有效的修改后提交您的更改。 |
你可能认为困难的部分是“我如何知道我正在使用一个有效的属性索引器?”答案是你需要了解他们。一份相当详尽的清单如下:然而,它们还有几十种,但这些是更常用的:
......@@ -114,17 +114,17 @@
```
![](../Images/image011.png)
![](img/image011.png)
图 6:目录对象的所有属性
上图显示了用户对象的所有属性,这些属性代表了我当前在机器上使用的帐户。输出的格式如下:
| ![](../Images/tip.png) | 提示: 属性名称 : 属性值 |
| ![](img/tip.png) | 提示: 属性名称 : 属性值 |
通过使用前面指出的代码,我们现在可以编辑帐户的描述。这里有一张证明我们手术成功完成的图片。
![](../Images/image012.png)
![](img/image012.png)
图 7:我们已经成功编辑了一个属性
......@@ -154,7 +154,7 @@
我们得到这个输出(其中 true 当然意味着对象存在,在这个例子中是一个同名的用户):
![](../Images/image013.png)
![](img/image013.png)
图 8:用户存在
......@@ -218,7 +218,7 @@
这就是我们得到的输出:
![](../Images/image014.png)
![](img/image014.png)
图 9:创建的新用户
......@@ -366,7 +366,7 @@ SetPassword 用于执行用户密码的管理重置,通常由管理员执行
```
| ![](../Images/note.png) | 注意:上面的例子是用于创建用户对象的稍加修改的版本。 |
| ![](img/note.png) | 注意:上面的例子是用于创建用户对象的稍加修改的版本。 |
显然,我们可能希望做一些不同于简单地打印和重新抛出 InnerException 的事情。这里的重点是 InnerException 包含我们感兴趣的信息。这里的另一个问题是,从 ADSI 回来的例外从模糊的有益的到真正令人困惑的。我们不会试图列出所有这些,但这里有一些提示。
......@@ -429,7 +429,7 @@ SetPassword 用于执行用户密码的管理重置,通常由管理员执行
不幸的是,并不是所有的操作都可以通过修改对象的属性来完成;事实上,有时有必要修改对象的安全描述符,以便允许某种操作。这些操作是通过向所需对象的自由访问控制列表(DACL)添加访问控制条目(ACE)来完成的。
| ![](../Images/note.png) | 注意:在的 1.0 和 1.1 版本中。NET 框架,不支持修改活动目录对象的安全描述符。活动目录对象的安全描述符可以通过调用 ADSI 并使用 IADsSecurityDescriptor 和相关接口来操作。英寸 NET Framework 2.0,这已经改变了,因为命名空间包含几个类,允许在不调用 ADSI 的情况下操作安全描述符。 |
| ![](img/note.png) | 注意:在的 1.0 和 1.1 版本中。NET 框架,不支持修改活动目录对象的安全描述符。活动目录对象的安全描述符可以通过调用 ADSI 并使用 IADsSecurityDescriptor 和相关接口来操作。英寸 NET Framework 2.0,这已经改变了,因为命名空间包含几个类,允许在不调用 ADSI 的情况下操作安全描述符。 |
与活动目录对象安全描述符一起使用的主要类是**活动目录安全**;它具有允许读取和修改对象的自由访问控制列表和辅助访问控制列表的方法和属性。自由访问控制列表中的访问控制条目被称为访问规则。代表访问规则的主要类是**活动目录访问规则**类。
......
此差异已折叠。
......@@ -8,7 +8,7 @@
在深入探讨这意味着什么之前,让我们先来回顾一下数据是如何存储在数组中的。
![](../Images/image001.png)
![](img/image001.png)
图 1:存储在数组中的整数数据
......@@ -93,7 +93,7 @@
链表数据结构的核心是`Node`类。节点是一个容器,它提供了存储数据和连接其他节点的能力。
![](../Images/image002.png)
![](img/image002.png)
图 2:链表节点包含数据和指向下一个节点的属性
......@@ -300,13 +300,13 @@
在谈论`Remove`算法之前,我们先来看看它试图实现什么。在下图中,列表中有四个节点。我们希望移除值为 3 的节点。
![](../Images/image003.png)
![](img/image003.png)
图 3:一个包含四个值的链表
移除完成后,列表将被修改,使得值为 2 的节点上的`Next`属性指向值为 4 的节点。
![](../Images/image004.png)
![](img/image004.png)
图 4:移除了 3 个节点的链表
......@@ -513,7 +513,7 @@
为了创建一个双向链表,我们需要首先修改我们的`LinkedListNode`类,以获得一个名为`Previous`的新属性。`Previous`会像`Next`一样行动,只是它会指向列表中的前一个节点。
![](../Images/image005.png)
![](img/image005.png)
图 5:使用上一个节点属性的双向链表
......
......@@ -149,7 +149,7 @@ Java 使用了类似的方法,但是数组的增长稍微慢一点。它用来
该算法的增长曲线较慢,这意味着它偏向于以更多分配为代价来减少内存开销。让我们看看这两种算法对于一个增加了 20 多万个项目的`ArrayList`的增长曲线。
![](../Images/image006.png)
![](img/image006.png)
图 6:200,000 多个项目的 Mono/Rotor 与 Java 的增长曲线
......@@ -182,15 +182,15 @@ Java 使用了类似的方法,但是数组的增长稍微慢一点。它用来
在下面的示例中,有一个容量为五项的数组,其中四项正在使用中。值“3”将作为数组中的第三项插入(索引 2)。
![](../Images/image007.png)
![](img/image007.png)
图 7:插入前的阵列(末端有一个开放的插槽)
![](../Images/image008.png)
![](img/image008.png)
图 8:向右移动后的数组
![](../Images/image009.png)
![](img/image009.png)
图 9:在开放插槽中添加了新项目的数组
......@@ -244,15 +244,15 @@ Java 使用了类似的方法,但是数组的增长稍微慢一点。它用来
在索引处移除本质上是`Insert`操作的反向。该项将从数组中移除,数组将向左移动。
![](../Images/image009.png)
![](img/image009.png)
图 10:移除值 3 之前的数组
![](../Images/image008.png)
![](img/image008.png)
图 11:移除了值 3 的数组
![](../Images/image007.png)
![](img/image007.png)
图 12:阵列向左移动,释放了最后一个插槽
......
......@@ -14,13 +14,13 @@
我们从一个空盘子架开始。
![](../Images/image010.jpg)
![](img/image010.jpg)
图 13:空的板堆(弹簧没有支撑板)
然后我们按照这个顺序在架子上添加一个红色、一个蓝色和一个绿色的盘子。
| ![](../Images/image011.jpg) | ![](../Images/image012.jpg) | ![](../Images/image013.jpg) |
| ![](img/image011.jpg) | ![](img/image012.jpg) | ![](img/image013.jpg) |
图 14:将红色、蓝色和绿色板添加到板架中
......@@ -588,7 +588,7 @@ deq 经常被用来实现其他数据结构。
```
![](../Images/image014.png)
![](img/image014.png)
图 15:向 deque 的前面添加一个值
......@@ -597,7 +597,7 @@ deq 经常被用来实现其他数据结构。
```
![](../Images/image015.png)
![](img/image015.png)
图 16:向 deque 的末尾添加一个值
......@@ -606,7 +606,7 @@ deq 经常被用来实现其他数据结构。
```
![](../Images/image016.png)
![](img/image016.png)
图 17:在 deque 的前面添加另一个值;头部索引环绕
......@@ -617,7 +617,7 @@ deq 经常被用来实现其他数据结构。
```
![](../Images/image017.png)
![](img/image017.png)
图 18:向 deque 的末尾添加一个值
......@@ -634,7 +634,7 @@ deq 经常被用来实现其他数据结构。
```
![](../Images/image018.png)
![](img/image018.png)
图 19:在展开的 deque 的末尾添加一个值
......@@ -645,7 +645,7 @@ deq 经常被用来实现其他数据结构。
```
![](../Images/image019.png)
![](img/image019.png)
图 20:从展开的视图中移除第一个项目
......@@ -654,7 +654,7 @@ deq 经常被用来实现其他数据结构。
```
![](../Images/image020.png)
![](img/image020.png)
图 21:从展开的列表中移除最后一项
......
......@@ -10,7 +10,7 @@
树是一种数据结构,其中每个节点都有 0 个或更多的子节点。例如,我们可能有这样一棵树:
![](../Images/image021.jpg)
![](img/image021.jpg)
图 22:组织树结构
......@@ -28,7 +28,7 @@
让我们看看使用这些规则构建的树:
![](../Images/image022.png)
![](img/image022.png)
图 23:二叉查找树
......@@ -238,31 +238,31 @@
#### 情况 1:要删除的节点没有合适的子节点。
![](../Images/image023.png)
![](img/image023.png)
图 24:案例 1—要删除的节点没有合适的子节点
在这种情况下,移除操作可以简单地将左边的子节点(如果有)移动到被移除节点的位置。生成的树如下所示:
![](../Images/image024.png)
![](img/image024.png)
图 25:案例 1—删除后的树状态
#### 情况 2:要移除的节点有一个右子节点,而右子节点没有左子节点。
![](../Images/image025.png)
![](img/image025.png)
图 26:案例 2—要移除的节点有一个没有左子节点的右子节点
在这种情况下,我们希望将被移除节点的右子节点(6)移动到被移除节点的位置。生成的树如下所示:
![](../Images/image026.png)
![](img/image026.png)
图 27:案例 2—删除后的树状态
#### 情况 3:要移除的节点有一个右子节点,而右子节点又有一个左子节点。
![](../Images/image027.png)
![](img/image027.png)
图 28:案例 3—要移除的节点有一个右子节点和一个左子节点
......@@ -277,7 +277,7 @@
删除节点后,树将如下所示:
![](../Images/image028.png)
![](img/image028.png)
图 29:案例 3—移除节点后的树
......@@ -505,7 +505,7 @@
每次遍历的`Order`部分将指示下一棵树遍历的顺序。
![](../Images/image029.png)
![](img/image029.png)
图 30:遍历排序结果的示例树
......
......@@ -94,7 +94,7 @@
虽然这在某些情况下可能是可以接受的,但这不是我们要实现的行为。想象一个集合,包含当地大学的所有学生。允许同一个学生被添加到集合中两次是不符合逻辑的。事实上,尝试这样做很可能是一个错误(在这个实现中将被这样对待)。
![](../Images/Note.png)注:`Add`采用 [`Contains`](#_Contains_1) 法。
![](img/Note.png)注:`Add`采用 [`Contains`](#_Contains_1) 法。
```cs
public void Add(T item)
......@@ -196,13 +196,13 @@
例如,给定两组(每组用红色表示):
![](../Images/image031.jpg)
![](img/image031.jpg)
图 31:联合操作之前的两个输入集
当执行联合操作时,输出集包含两个集中的所有项目。如果两个集中都存在任何项目,则每个项目只有一个副本被添加到输出集中。
![](../Images/image032.jpg)
![](img/image032.jpg)
图 32:联合操作后的输出集(返回的项目是黄色的)
......@@ -235,7 +235,7 @@
交集是两个集合“相交”的点,即它们的公共成员。使用联合示例中的维恩图,这里显示了两个集合的交集:
![](../Images/image033.jpg)
![](img/image033.jpg)
图 33:两个输入集的交集显示为黄色
......@@ -268,7 +268,7 @@
两个集合之间的差异或集合差异是存在于第一个集合(调用`Difference`方法的集合)中,但不存在于第二个集合(方法的参数)中的项目。此处显示了该方法的维恩图,返回的集合为黄色:
![](../Images/image034.jpg)
![](img/image034.jpg)
图 34:两组之间的设置差异
......@@ -298,7 +298,7 @@
两个集合的对称差是这样一个集合,它的成员是那些只存在于其中一个集合中的项。此处显示了该方法的维恩图,返回的集合为黄色:
![](../Images/image035.jpg)
![](img/image035.jpg)
图 35:两组对称差
......
......@@ -32,25 +32,25 @@
考虑以下未排序的整数数组:
![](../Images/image036.png)
![](img/image036.png)
图 36:未排序的整数数组
在第一次通过数组时,比较值 3 和 7。由于 7 大于 3,因此不执行交换。接下来,比较 7 和 4。7 大于 4,因此值被交换,从而将 7 移近数组的末尾一步。该阵列现在如下所示:
![](../Images/image037.png)
![](img/image037.png)
图 37:4 和 7 交换了位置
重复这个过程,7 最终会与更大的 8 进行比较,因此无法执行交换,传递会在数组的末尾结束。在第 1 遍结束时,数组如下所示:
![](../Images/image038.png)
![](img/image038.png)
图 38:第 1 遍结束时的数组
因为至少执行了一次交换,所以将执行另一次传递。第二次传球后,6 号位已经就位。
![](../Images/image039.png)
![](img/image039.png)
图 39:第 2 遍结束时的数组
......@@ -92,7 +92,7 @@
重要的概念是,插入排序是通过对遇到的项目进行排序来工作的。因为它从左到右处理数组,所以我们知道当前索引左边的所有内容都是排序的。此图演示了遇到每个索引时数组是如何排序的:
![](../Images/image040.png)
![](img/image040.png)
图 40:通过插入排序处理的数组。
......@@ -100,7 +100,7 @@
我们来看一个具体的例子。下面是一个未排序的数组,将使用插入排序进行排序。
![](../Images/image041.png)
![](img/image041.png)
图 41:未排序的整数数组
......@@ -114,15 +114,15 @@
此方法确定索引 1(在 3 和 7 之间)是合适的插入点。然后,插入算法(图 43 后面的代码示例中的`Insert`方法)通过从数组中移除要插入的值并将所有值从插入点向右移动移除的项来执行插入。该阵列现在如下所示:
![](../Images/image037.png)
![](img/image037.png)
图 42:第一次插入后的数组算法
从索引 0 到 2 的数组现在已知是已排序的,从索引 3 到结尾的所有内容都是未知的。该过程现在再次从索引 3 开始,索引 3 的值为 4。随着算法的继续,在对数组进行排序之前,会出现以下插入。
![](../Images/image042.png)
![](img/image042.png)
![](../Images/image043.png)
![](img/image043.png)
图 43:进一步插入算法后的数组
......@@ -203,7 +203,7 @@
让我们看看如何使用我们一直在使用的相同的未排序数组。
![](../Images/image041.png)
![](img/image041.png)
图 44:未排序的整数数组
......@@ -213,7 +213,7 @@
第二遍将确定 4 是未排序范围中的最小值,并将第二个插槽中的值与 4 所在插槽中的值进行交换(交换 4 和 7)。第二遍完成后,值 4 将被插入其排序位置。
![](../Images/image044.png)
![](img/image044.png)
图 45:第二遍后的数组
......@@ -221,9 +221,9 @@
再经过两次后,数组被排序:
![](../Images/image045.png)
![](img/image045.png)
![](../Images/image046.png)
![](img/image046.png)
图 46:排序数组
......@@ -285,31 +285,31 @@
让我们从下面的数组开始:
![](../Images/image047.png)
![](img/image047.png)
图 47:未排序的整数数组
现在我们把阵列切成两半:
![](../Images/image048.png)
![](img/image048.png)
图 48:未排序的数组被切成两半
现在,这两个阵列都被反复切成两半,直到每个项目都独立出来:
![](../Images/image049.png)
![](img/image049.png)
图 49:未排序的数组被切成两半,直到每个索引都独立出来
现在数组被分成尽可能小的部分,按照排序顺序将这些部分合并在一起的过程就发生了。
![](../Images/image050.png)
![](img/image050.png)
图 50:数组分为两组
单个项目变成了两个有序的组,这两个组合并成四个有序的组,然后它们最终全部合并成一个最终的有序数组。
![](../Images/image051.png)
![](img/image051.png)
图 51:排列成四组的数组(顶部)和完成的排序(底部)
......@@ -391,7 +391,7 @@
让我们对以下数组执行快速排序:
![](../Images/image052.png)
![](img/image052.png)
图 52:未排序的整数数组
......@@ -399,7 +399,7 @@
`int pivotIndex = _pivotRng.Next(left, right);`
![](../Images/image053.png)
![](img/image053.png)
图 53:选择随机分区索引
......@@ -407,7 +407,7 @@
在图 57 所示的示例代码中,通过`partition`方法交换值。
![](../Images/image054.jpg)
![](img/image054.jpg)
图 54:将值移动到分区值的左侧和右侧
......@@ -415,19 +415,19 @@
通过递归调用每个数组分区的`quicksort`方法,在示例代码中完成重复。请注意,这一次左数组在索引 1 处被分区,值为 5。将值移动到适当位置的过程会将值 5 移动到另一个索引。我指出这一点是为了强调您选择的是分区值,而不是分区索引。
![](../Images/image055.png)
![](img/image055.png)
图 55:重复枢轴和分区
再次快速排序:
![](../Images/image056.png)
![](img/image056.png)
图 56:再次重复枢轴和分区
最后一次快速排序:
![](../Images/image057.png)
![](img/image057.png)
图 57:再次重复枢轴和分区
......
......@@ -14,7 +14,7 @@
我们先来看看内存中的有序链表。
![](../Images/image001.png)
![](img/image001.png)
图 1:内存中表示的排序链表
......@@ -22,19 +22,19 @@
那我们怎么能把它减半呢?如果我们能跳过所有其他节点呢?显然,我们无法摆脱基本的`Next`指针——枚举每一项的能力至关重要。但是如果我们有另一组跳过所有其他节点的指针呢?现在我们的列表可能是这样的:
![](../Images/image002.png)
![](img/image002.png)
图 2:指针跳过每隔一个节点的排序链表
通过使用更宽的链接,我们的搜索将能够执行一半的比较。下图中显示的橙色路径演示了搜索路径。橙色的点代表进行比较的点——这是我们在确定搜索算法的复杂性时测量的比较。
![](../Images/image003.png)
![](img/image003.png)
图 3:新指针的搜索路径
*O* ( *n* )现在大致是 *O* ( *n* /2)。这是一个不错的改进,但是如果我们再增加一层会发生什么呢?
![](../Images/image004.png)
![](img/image004.png)
图 4:添加额外的链接层
......@@ -58,19 +58,19 @@
让我们采用三级跳过列表,并从列表中移除值为 5 的节点。
![](../Images/image005.png)
![](img/image005.png)
图 5:删除了 5 个节点的跳过列表
随着 5 的消失,我们遍历三级链接的能力也消失了,但是我们仍然能够在四次比较中找到值 8(基本上是 *O* ( *n* /2))。现在让我们去掉 7。
![](../Images/image006.png)
![](img/image006.png)
图 6:删除了 5 个和 7 个节点的跳过列表
我们现在只能使用单个二级链接,我们的算法正在快速接近 *O* ( *n* )。一旦我们移除值为 3 的节点,我们就会在那里。
![](../Images/image007.png)
![](img/image007.png)
图 7:删除了 3、5 和 7 个节点的跳过列表
......@@ -82,7 +82,7 @@
使用随机方法,我们的列表可能如下所示:
![](../Images/image008.png)
![](img/image008.png)
图 8:具有随机高度的跳过列表
......@@ -211,7 +211,7 @@
更进一步,下图显示了调用`PickRandomLevel`一百万次的结果。你可以看到所有的 100 万都至少有 1 米高,从那里开始的缩放比例完全符合我们的预期。
![](../Images/image009.png)
![](img/image009.png)
图 9:最小高度值被选取一百万次
......@@ -357,7 +357,7 @@
下图演示了如何在跳过列表中搜索数字 5。
![](../Images/image010.png)
![](img/image010.png)
图 10:在跳过列表中搜索值 5
......@@ -502,7 +502,7 @@
这可以很容易地在 *O* ( *n* )时间内通过简单地行走第一级链接来实现,但是优化的方法是跟踪每个链接的长度,并使用该信息行走到适当的链接。示例列表可能如下所示:
![](../Images/image011.png)
![](img/image011.png)
图 11:带有链接长度的跳过列表
......
......@@ -57,13 +57,13 @@
容量是数组可能容纳的项目数。例如,以下空阵列的容量为 10:
![](../Images/image012.png)
![](img/image012.png)
图 12:容量为 10 的阵列
填充因子是已填充(使用中)的数组项目的百分比。例如,以下数组的填充因子为 0.40 (40%):
![](../Images/image013.png)
![](img/image013.png)
图 13:容量为 10、填充系数为 0.40 (40%)的阵列。
......@@ -246,7 +246,7 @@ DJB2 唯一值:99.88282%
下一个打开的插槽方法在后备数组中向前移动,搜索下一个打开的插槽,并将项目放在该位置。例如,在下图中,值 V1 和 V2 具有相同的哈希值。由于 V1 已经在散列表中,V2 前进到散列表中的下一个空位。
![](../Images/image014.jpg)
![](img/image014.jpg)
图 14:V1 和 V2 哈希值的冲突
......@@ -262,7 +262,7 @@ DJB2 唯一值:99.88282%
处理冲突的另一种方法是让哈希表后备数组中的每个索引成为节点的链表。发生冲突时,新值会添加到链接列表中。例如:
![](../Images/image015.jpg)
![](img/image015.jpg)
图 15: V2 被添加到 V1 之后的链表中
......@@ -1060,7 +1060,7 @@ DJB2 唯一值:99.88282%
| 行为 | 如果哈希表包含与提供的值匹配的值,则返回`true`。 |
| 表演 | *O* ( *n* ) |
| ![](../Images/Note.png) | 注意:重要的是要记住,虽然哈希表不包含冲突的键,但它可能包含相同值的多个实例。 |
| ![](img/Note.png) | 注意:重要的是要记住,虽然哈希表不包含冲突的键,但它可能包含相同值的多个实例。 |
```cs
/// <summary>
......@@ -1105,7 +1105,7 @@ DJB2 唯一值:99.88282%
| 行为 | 返回哈希表中包含的项数。 |
| 表演 | *O* (1) |
| ![](../Images/Note.png) | 注意:后备数组的容量和哈希表中存储的项目数不一样(填充因子小于 1 的永远不会一样)。 |
| ![](img/Note.png) | 注意:后备数组的容量和哈希表中存储的项目数不一样(填充因子小于 1 的永远不会一样)。 |
```cs
/// <summary>
......
......@@ -22,17 +22,17 @@
让我们看看一些无效的堆树:
![](../Images/image017.jpg)
![](img/image017.jpg)
图 16:无效,因为子值 8 大于父值 6
![](../Images/image018.jpg)
![](img/image018.jpg)
图 17:无效,因为子节点填充在右边,而不是左边
现在是有效的堆:
![](../Images/image019.jpg)
![](img/image019.jpg)
图 18:遵循属性和完整性规则的有效堆
......@@ -44,13 +44,13 @@
让我们先来看一棵树,它的节点是根据它们在树中的级别来着色的。
![](../Images/image020.jpg)
![](img/image020.jpg)
图 19:基于级别的颜色树
这是一个完整的树,有三个层次的七个节点。我们将此逐级放入一个数组中,如下所示:
![](../Images/image021.jpg)
![](img/image021.jpg)
图 20:投影到数组中的二叉树
......@@ -58,11 +58,11 @@
让我们看一个具体的例子,其中这个有效的堆映射成一个数组。
![](../Images/image022.jpg)
![](img/image022.jpg)
图 21:作为二叉树的有效堆
![](../Images/image023.jpg)
![](img/image023.jpg)
图 22:映射到数组中的有效堆树
......@@ -158,13 +158,13 @@
例如,让我们看看之前的有效堆:
![](../Images/image024.jpg)
![](img/image024.jpg)
图 23:有效堆
我们将把值 10 添加到堆中。使用所描述的算法,我们从将值添加到后备数组的末尾开始。由于我们在数组中存储了一个完整的树,这意味着我们在最后一层最左边的空闲槽中添加了一个新节点。
![](../Images/image025.jpg)
![](img/image025.jpg)
图 24:向有效堆中添加 10
......@@ -177,7 +177,7 @@
您应该注意到这违反了堆属性,该属性要求每个节点的子节点小于或等于父节点的值。在这种情况下,值 5 有一个值为 10 的子级。要解决这个问题,我们需要交换节点,如下所示:
![](../Images/image026.jpg)
![](img/image026.jpg)
图 25:交换 10 和 5 个节点
......@@ -190,7 +190,7 @@
我们现在已经修复了 10 和 5 之间的关系,但是 10 的父节点是 8,这违反了堆属性,所以我们也需要交换这些。
![](../Images/image027.jpg)
![](img/image027.jpg)
图 26:交换 10 和 8 个节点
......@@ -248,7 +248,7 @@
由于该值正在被移除,数组索引 0 现在是空闲的。为了确保我们的阵列没有任何间隙,我们需要向其中移动一些东西。我们要做的是抓取数组中的最后一项,并将其向前移动到索引 0。在树形视图中,它看起来像这样:
![](../Images/image028.jpg)
![](img/image028.jpg)
图 27:将最后一个数组项移动到索引 0
......@@ -263,13 +263,13 @@
我们需要做的是将较小的父节点与其子节点交换,直到满足堆属性。这就留下了一个问题:我们和哪个孩子交换?答案是我们总是和最大的孩子交换。想想如果我们和小一点的孩子交换会发生什么。树会从一个无效状态切换到另一个无效状态。例如:
![](../Images/image029.jpg)
![](img/image029.jpg)
图 28:用较小的值交换不会创建有效的堆
我们还没有解决问题!相反,我们需要像这样与两个孩子中较大的一个交换:
![](../Images/image030.jpg)
![](img/image030.jpg)
图 29:与最大的子代交换创建一个有效的堆
......
......@@ -16,7 +16,7 @@ AVL 树是一个自平衡的二叉查找树。它是以它的发明者 G. M .阿
可以测量任何两个相关节点之间的距离。
![](../Images/image031.jpg)
![](img/image031.jpg)
图 30:节点高度
......@@ -32,7 +32,7 @@ AVL 树是一个自平衡的二叉查找树。它是以它的发明者 G. M .阿
这意味着左边的子节点和右边的子节点的最大高度相差不超过一个,并且该规则适用于树中的每个节点。看到一些无效的平衡树可能有助于使这一点更加清晰。
![](../Images/image032.jpg)
![](img/image032.jpg)
图 31:根节点的左侧高度为 2,右侧高度为 0。
......@@ -40,7 +40,7 @@ AVL 树是一个自平衡的二叉查找树。它是以它的发明者 G. M .阿
请注意,节点 4 本身是平衡的,因为它的左右高度都是一。
![](../Images/image033.jpg)
![](img/image033.jpg)
图 32:根节点是平衡的,但是它的右子节点不是
......@@ -56,11 +56,11 @@ AVL 树使用两个基本的节点旋转(左和右)和两个使用基本旋转
右旋转是从左向右旋转节点的行为,如下图所示:
![](../Images/image034.jpg)
![](img/image034.jpg)
图 33:右旋转将根的左子级移动到根位置
![](../Images/image035.jpg)
![](img/image035.jpg)
图 34:右旋转将根的左子级的右子级移动到旧根的左子级
......@@ -76,11 +76,11 @@ AVL 树使用两个基本的节点旋转(左和右)和两个使用基本旋转
向左旋转是旋转一个向右运行的节点系列的行为,这样系列的中间部分就变成了新的根。
![](../Images/image036.jpg)
![](img/image036.jpg)
图 35:向左旋转将 B 节点从序列的中间移到根
![](../Images/image037.jpg)
![](img/image037.jpg)
图 36:向左旋转,新根的左子级移动到旧根的右子级
......@@ -96,7 +96,7 @@ AVL 树使用两个基本的节点旋转(左和右)和两个使用基本旋转
左右旋转对根节点的右子节点应用右旋转,然后在根节点应用左旋转。我们究竟为什么要这样做?考虑以下树结构:
![](../Images/image038.jpg)
![](img/image038.jpg)
图 37:需要左右旋转的不平衡树
......@@ -110,7 +110,7 @@ AVL 树使用两个基本的节点旋转(左和右)和两个使用基本旋转
不幸的是,当我们这样做时,结果树仍然是不平衡的。
![](../Images/image039.jpg)
![](img/image039.jpg)
图 38:应用左旋转后,树仍然不平衡
......@@ -118,7 +118,7 @@ AVL 树使用两个基本的节点旋转(左和右)和两个使用基本旋转
因为我们正在执行右子节点(C)的右旋转,所以我们可以暂时忽略根节点(A)。
![](../Images/image040.jpg)
![](img/image040.jpg)
图 39:右旋转前后的右子树。
......@@ -126,13 +126,13 @@ AVL 树使用两个基本的节点旋转(左和右)和两个使用基本旋转
在较大的树的上下文中,我们的旋转变换了树,如下所示:
![](../Images/image041.jpg)
![](img/image041.jpg)
图 40:在右边的孩子身上进行的右旋转
结果树(A > B > C)是我们知道如何使用左旋转算法旋转的东西。整个过程如下所示:
![](../Images/image042.jpg)
![](img/image042.jpg)
图 41:整个左右旋转过程
......@@ -140,7 +140,7 @@ AVL 树使用两个基本的节点旋转(左和右)和两个使用基本旋转
左右旋转只是与左右旋转相反。我们从一个不平衡的树开始,其中根节点有一个左子节点,它有一个右子节点,但没有左子节点。一旦我们确定了这种情况,解决方案是对左子对象应用左旋转,然后对根对象应用右旋转。整个左右旋转过程如下图所示:
![](../Images/image043.jpg)
![](img/image043.jpg)
图 42:整个左右旋转过程
......@@ -155,11 +155,11 @@ AVL 树使用两个基本的节点旋转(左和右)和两个使用基本旋转
平衡系数是左右高度之差。例如,如果树的右子节点高度为 5,左子节点高度为 3,则平衡因子为 2。同样,如果高度颠倒,平衡系数将为-2。
![](../Images/image044.jpg)
![](img/image044.jpg)
图 43:右重节点(A)。它的右边的孩子(C)有一个平衡因子-1,因为平衡因子是 C (0)的右边高度和左边高度(1)的差值。
![](../Images/image045.jpg)
![](img/image045.jpg)
图 44:一个右重节点(A)。它的右孩子(B)的平衡系数为 1,因为它的右孩子身高(1)和左孩子身高(0)之差为 1。
......@@ -247,7 +247,7 @@ AVL 树使用两个基本的节点旋转(左和右)和两个使用基本旋转
这个决定是通过查看根节点的左或右子节点做出的,这取决于它是右还是左重。接下来,看看平衡因素。这两个比较用于选择旋转算法。
| ![](../Images/Note.png) | 注意:在这个实现中,树的高度是通过遍历树以找到最长的路径来确定的。这在概念上很简单,但却没有它应有的效率。作为一个练习,考虑可以更有效地确定任何节点高度的其他方法。 |
| ![](img/Note.png) | 注意:在这个实现中,树的高度是通过遍历树以找到最长的路径来确定的。这在概念上很简单,但却没有它应有的效率。作为一个练习,考虑可以更有效地确定任何节点高度的其他方法。 |
```cs
internal void Balance()
......
......@@ -10,7 +10,7 @@ B 树是一种有序、平衡的树结构。b 树通常用于访问存储在磁
了解 B 树结构最简单的方法可能是看一下 B 树,并讨论它与我们已经熟悉的树结构的不同之处:二叉查找树。下面是一个 B 树的例子:
![](../Images/image046.jpg)
![](img/image046.jpg)
图 45:一个示例 B 树
......@@ -45,11 +45,11 @@ B 树结构强制执行树中的每个叶节点深度相同并且每个叶节点
我们在图 45 中看到的树有四个叶节点,它们都在相同的深度 2。下面是两个无效 B 树的例子。
![](../Images/image047.jpg)
![](img/image047.jpg)
图 46:无效的 B 树。叶节点(绿色)并不都在同一层
![](../Images/image048.jpg)
![](img/image048.jpg)
图 47:无效的 B 树。叶节点(绿色)处于相同高度,但有一个是空的或缺失的
......@@ -72,7 +72,7 @@ B 树结构强制执行树中的每个叶节点深度相同并且每个叶节点
为了了解它是如何工作的,让我们通过几个场景搜索下图中的树:
![](../Images/image049.jpg)
![](img/image049.jpg)
图 48:用于搜索的 B 树
......@@ -108,7 +108,7 @@ B 树结构强制执行树中的每个叶节点深度相同并且每个叶节点
下推一个值是一个过程,其中两个子节点合并在一起,父节点的值作为中间值下推到结果节点中。让我们看看这意味着什么。我们从下面的树开始:
![](../Images/image050.jpg)
![](img/image050.jpg)
图 49:向下推的 B 树
......@@ -126,7 +126,7 @@ B 树结构强制执行树中的每个叶节点深度相同并且每个叶节点
这将生成如下所示的树:
![](../Images/image051.jpg)
![](img/image051.jpg)
图 50:向下推 B 树
......@@ -145,7 +145,7 @@ B 树结构强制执行树中的每个叶节点深度相同并且每个叶节点
考虑下面的树:
![](../Images/image052.jpg)
![](img/image052.jpg)
图 51:用于旋转的 B 树
......@@ -153,13 +153,13 @@ B 树结构强制执行树中的每个叶节点深度相同并且每个叶节点
解决这个问题的关键是[1,2]的兄弟节点[4,5,6]具有超过 *T* - 1 的值,因此可以从其获取值而不违反结构规则。这是通过旋转我们想要通过父代的值来完成的,如下所示。
![](../Images/image053.jpg)
![](img/image053.jpg)
图 52:旋转前的 B 树
我们要旋转的值(4)将旋转到父节点,父值(3)将旋转到子节点,得到的结果树为:
![](../Images/image054.jpg)
![](img/image054.jpg)
图 53:旋转后的 B 树
......@@ -182,13 +182,13 @@ B 树结构强制执行树中的每个叶节点深度相同并且每个叶节点
例如,给定以下树:
![](../Images/image055.jpg)
![](img/image055.jpg)
图 54:用于拆分的 B 树
我们希望拆分节点[1,2,3,4,5],并将其中间值 3 移动到父节点,从而创建新的根节点[3,6]。这将导致以下树:
![](../Images/image056.jpg)
![](img/image056.jpg)
图 55:拆分左侧子节点后的 B-树
......@@ -225,7 +225,7 @@ B 树结构强制执行树中的每个叶节点深度相同并且每个叶节点
我们从最小因子为 3 的树开始。这意味着根节点中可以有 0(空)到 5(满)个值。在这个场景中,整个树中有 5 个值,它们都存在于根节点中。起始树是:
![](../Images/image057.jpg)
![](img/image057.jpg)
图 56:所有值都在根节点的 B 树
......@@ -233,7 +233,7 @@ B 树结构强制执行树中的每个叶节点深度相同并且每个叶节点
为此,根节点的中值 3 被拉进一个新的根节点,相邻的值[1,2]和[4,5]成为根的子节点。完成后,树如下所示:
![](../Images/image058.jpg)
![](img/image058.jpg)
图 57:分割根节点后的 B 树
......
......@@ -10,7 +10,7 @@ ECMAScript 6 (ES6),也称为 ECMAScript 2015,为开发人员带来了长期
使用您最喜欢的 transpiler 在浏览器中运行大多数代码示例是可能的,但是为了保持一致,我们将使用前面提到的依赖项。我们的目标是为您提供一个快速简单的工具,帮助您充分了解 ES6 中的新功能。
| ![](../images/00003.gif) | 注:除非另有说明,我们将使用崇高的文本连同巴别塔和节点 |
| ![](img/00003.gif) | 注:除非另有说明,我们将使用崇高的文本连同巴别塔和节点 |
以下是让您的机器为使用本书中的代码示例做好准备的步骤:
......@@ -60,11 +60,11 @@ ECMAScript 6 (ES6),也称为 ECMAScript 2015,为开发人员带来了长期
```
| ![](../images/00003.gif) | 注意:您可能需要在文件 test.js 中放置一个“use strict”语句作为第一行,以便运行脚本。 |
| ![](img/00003.gif) | 注意:您可能需要在文件 test.js 中放置一个“use strict”语句作为第一行,以便运行脚本。 |
12. 保存文件并构建。你应该在《崇高》中看到类似以下的东西:
![](../images/00004.jpeg)
![](img/00004.jpeg)
图 1:升华文本输出
......
......@@ -19,7 +19,7 @@
```
| ![](../images/00003.gif) | 注意:除非另有说明,否则我们将使用崇高作为代码片段的首选编辑器。在 Git 存储库中,您会在它们自己的章节中找到相应的片段。 |
| ![](img/00003.gif) | 注意:除非另有说明,否则我们将使用崇高作为代码片段的首选编辑器。在 Git 存储库中,您会在它们自己的章节中找到相应的片段。 |
现在,让我们尝试为`PI`变量重新赋值。我们可以通过在编辑器中添加以下几行来实现这一点:
......@@ -34,13 +34,13 @@
尝试运行前面的示例会产生语法错误。transpiler 足够聪明,能告诉我们原因。请看下图:
![](../images/00005.jpeg)
![](img/00005.jpeg)
图 2:常量只读
您可以在图 2 中看到,由于`PI`被声明为`constant`,因此它被认为是只读的。这很好,错误描述非常清楚。
| ![](../images/00003.gif) | 注意:const 关键字只能用于块范围区域。我们将在下一节研究块范围。 |
| ![](img/00003.gif) | 注意:const 关键字只能用于块范围区域。我们将在下一节研究块范围。 |
以前,JavaScript 开发人员在使用`var`关键字时必须处理吊装。在运行时,任何声明的变量都将被提升到执行上下文的顶部。让我们看几个在运行时如何处理`var`关键字的例子:
......@@ -186,7 +186,7 @@ ES6 中为正则表达式引入了两个新标志:
```
| ![](../images/00003.gif) | 注意:这个例子只会在微软 Edge 或者火狐上运行。 |
| ![](img/00003.gif) | 注意:这个例子只会在微软 Edge 或者火狐上运行。 |
我们从文本变量中的一个简单字符串开始。接下来,我们定义我们的正则表达式。如您所见,我们正在寻找以“行”结尾的匹配项,可能还有一个新行。然后我们执行正则表达式并抓取第一个匹配,它包含值`First`并记录`lastIndex`位置。
......@@ -223,7 +223,7 @@ ES6 中为正则表达式引入了两个新标志:
```
| ![](../images/00003.gif) | 注意:这个例子只会在微软 Edge 或者火狐上运行。 |
| ![](img/00003.gif) | 注意:这个例子只会在微软 Edge 或者火狐上运行。 |
以下是它产生的输出:
......@@ -264,4 +264,4 @@ True
第一行代码演示了将 Unicode 代码点与两个正常的 Unicode 转义进行比较。第二行和第三行只是演示如何呈现 Unicode 代码点。
| ![](../images/00003.gif) | 注意:不是所有浏览器都支持/y 或/u 标志。这些示例在火狐中使用过,但也可以在微软 Edge 中运行。 |
\ No newline at end of file
| ![](img/00003.gif) | 注意:不是所有浏览器都支持/y 或/u 标志。这些示例在火狐中使用过,但也可以在微软 Edge 中运行。 |
\ No newline at end of file
......@@ -744,7 +744,7 @@ Array.from(arrayLike[,mapFn[,thisArg]])
```
| ![](../images/00003.gif) | 注意:此示例仅适用于浏览器。 |
| ![](img/00003.gif) | 注意:此示例仅适用于浏览器。 |
Array.includes(searchElement[,fromIndex])
......@@ -1044,7 +1044,7 @@ Array.values()
```
| ![](../images/00003.gif) | 注意:此示例仅适用于浏览器。 |
| ![](img/00003.gif) | 注意:此示例仅适用于浏览器。 |
#### XMLHttpRequest 应用编程接口
......@@ -1071,7 +1071,7 @@ Array.values()
```
| ![](../images/00003.gif) | 注意:此示例仅适用于浏览器。 |
| ![](img/00003.gif) | 注意:此示例仅适用于浏览器。 |
#### 获取应用编程接口
......@@ -1088,7 +1088,7 @@ Array.values()
```
| ![](../images/00003.gif) | 注意:此示例仅适用于浏览器。 |
| ![](img/00003.gif) | 注意:此示例仅适用于浏览器。 |
#### 帆布
......@@ -1107,7 +1107,7 @@ Array.values()
```
| ![](../images/00003.gif) | 注意:此示例仅适用于浏览器。 |
| ![](img/00003.gif) | 注意:此示例仅适用于浏览器。 |
#### websocket
......@@ -1143,4 +1143,4 @@ Array.values()
```
| ![](../images/00003.gif) | 注意:此示例仅适用于浏览器。 |
\ No newline at end of file
| ![](img/00003.gif) | 注意:此示例仅适用于浏览器。 |
\ No newline at end of file
模块支持在不污染全局命名空间的情况下导出和导入值。我们已经通过像 AMD 和 CommonJS 这样的第三方库在 JavaScript 中拥有了这种能力,但是 ECMAScript 6 现在也拥有了这种能力。
| ![](../images/00003.gif) | 注意:本章处理实际文件,演示 ECMAScript 6 的导出和导入功能。所有“文件”引用都指向与代码片段标题同名的文件,格式如下:code-listing-xxx.js。 |
| ![](img/00003.gif) | 注意:本章处理实际文件,演示 ECMAScript 6 的导出和导入功能。所有“文件”引用都指向与代码片段标题同名的文件,格式如下:code-listing-xxx.js。 |
理解出口的最好方法是在行动中观察它。考虑以下文件:
......@@ -167,7 +167,7 @@
```
| ![](../images/00003.gif) | 注意:每个模块只能定义一个默认导出。 |
| ![](img/00003.gif) | 注意:每个模块只能定义一个默认导出。 |
通配符允许我们加载整个模块,并通过属性符号引用命名的导出。请看下面的例子:
......
代理允许创建具有宿主对象可用的全部行为的对象。它们可用于拦截、对象虚拟化、日志记录、分析等。代理被认为是元编程特性。
| ![](../images/00003.gif) | 注意:这是并非所有浏览器或实现都相同的领域之一。以下示例可以在最新版本的 Firefox 或可能的微软 Edge 下运行和测试。 |
| ![](img/00003.gif) | 注意:这是并非所有浏览器或实现都相同的领域之一。以下示例可以在最新版本的 Firefox 或可能的微软 Edge 下运行和测试。 |
## 代理
......
......@@ -28,4 +28,4 @@ ES6 提供尾调用优化(TCO),在这里您可以进行一些函数调用,
总而言之,如果在 return 语句之前发生的最后一件事是调用一个不需要访问任何当前局部变量的函数,那么 ES6 指定的解释器将通过重用堆栈帧来优化该调用。
| ![](../images/00003.gif) | 注意:在我们的浏览器有 ES6 解释器之前,这不太可能得到优化。 |
\ No newline at end of file
| ![](img/00003.gif) | 注意:在我们的浏览器有 ES6 解释器之前,这不太可能得到优化。 |
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册