来自 操作系统 2020-03-12 10:20 的文章
当前位置: 美高梅游戏平台网站 > 操作系统 > 正文

Rust 1.40.0 发布

Rust 1.40.0 已经正式发布。该版本的亮点包括有 #[non_exhaustive] 和 macros!() and #[attribute]s 的改进。

在本章中,我们将更多讨论有关在Visual LISP中使用ActiveX性能。首先,我们从ActiveX的技术环境开始,包括对象、对象模型、集合、属性、方法等方面。然后我们将深挖一些特定的ActiveX技术部分。了解ActiveX功能性是学习任何语言必不可少的工作。

具体更新内容如下:

ActiveX是一种面向对象的工具,也就是说它表现为对象的使用以及对象间的关联这种方式。在这里我不想去解释面向对象的问题,这些东西最好留给那些更专业的教科书去讲。然而,我将尝试给一些基本的面向对象方面的描述,以便大家有一个基本的了解。

#[non_exhaustive] 结构,枚举和变体

当属性#[non_exhaustive]附加到struct或的变体时enum,它将防止定义它的板条箱外部的代码构造所述struct或变体。为了避免将来损坏,还防止其他包装箱在田地上进行彻底匹配。以下示例说明了beta取决于的错误alpha

在面向对象的环境中,任何事物都是以Classes(类)开始的。类是抽象的框架,用于描述对象应是什么样的,以及他们应该如何表现和相互联系的。在某种意义上说类定义了对象类型的种类。例如,一辆汽车是属于交通工具类。交通工具是父类,而汽车是子类。反之,你可取得更多特定的和额外定义的子类,例如旅行车、面包车和运动型轿车。

// alpha/lib.rs:

#[non_exhaustive]
struct Foo {
    pub a: bool,
}

enum Bar {
    #[non_exhaustive]
    Variant { b: u8 }
}

fn make_foo() -> Foo { ... }
fn make_bar() -> Bar { ... }

// beta/lib.rs:

let x = Foo { a: true }; //~ ERROR
let Foo { a } = make_foo(); //~ ERROR

// `beta` will still compile when more fields are added.
let Foo { a, .. } = make_foo(); //~ OK


let x = Bar::Variant { b: 42 }; //~ ERROR
let Bar::Variant { b } = make_bar(); //~ ERROR
let Bar::Variant { b, .. } = make_bar(); //~ OK
                   // -- `beta` will still compile...

类不处理特定的实例,他们更注重于实例方面的描述,而非使用方面。当你使用类时,你可以说调用类的一个实例。调用类的结果经常是创建了一个对象。对象可以是单独的图元或包含了更多对象的容器。

幕后发生的事情是,#[non_exhaustive] struct或的构造函数的可见性enum降低到pub(crate),从而阻止了在定义它的板条箱外部进行访问。

对象
一个对象就是类的一个实例。对象有其固定的属性,同时也可以有固定的方法和事件。属性是定义对象怎样表现和反应的特性。方法是用于访问或修改对象属性或某些行为的内置函数。事件是由对象发送的通知,当执行或激活它们时,它用于响应特定动作。

更重要的方面是,#[non_exhaustive]也可以附加到enum美高梅游戏平台网站,自身上。从标准库中获取的示例是Ordering

就用上面的汽车类的例子来说,对象可以是特别的轿车。比如说,你的轿车,带有特别的配置(制造、型号、颜色、选配、以及序列号)。你可以说,你的轿车是汽车类的一个实例,或汽车类派生的一些类。

#[non_exhaustive]
pub enum Ordering { Relaxed, Release, Acquire, AcqRel, SeqCst }

图3-1 对象模型

#[non_exhaustive]在此上下文中的目的是确保可以随时间添加更多变体。这是通过防止其他包装箱从详尽模式实现match-ing上Ordering。也就是说,编译器将拒绝:

对象模型
对象模型是一个随意的架构,或类之间的分层定义的排列关系,意思就是可以从更高级别的类来获得一个对象。对象模型与访问它的语言或工具无关,它有自己的逻辑框架。不管你是使用Visual Basic、VBA、Visual LISP、Delphi、Java、C/C++、C#.NET或带有ActiveX接口的其它语言,它都将以相同的模型存在。这并不代表对象模型的所有特性都支持所有语言。某些特性在某些语言中可以访问或比较容易被访问,但在其它语言中可能就不行。

match ordering {
    // This is an error, since if a new variant is added,
    // this would suddenly break on an upgrade of the compiler.
    Relaxed | Release | Acquire | AcqRel | SeqCst => {
        /* logic */
    }
}

我们将对象模型比喻成一套房子,它由房间、门和窗组成。不同的人都可进入和使用房子,而他们都是面对同样的这套房子。在这种情况下,房子和房间就是对象模型,而人即是编程语言。这样,你应该懂吧。

取而代之的是,其他板条箱需要通过添加通配符来解决更多变体的可能性,例如_

类继承
对象模型通常都是从根或源对象开始。在AutoCAD中,源对象是AutoCAD Application (AutoCAD应用程序)对象,也被称之为AcadApplication对象。它提供了基本的属性、方法、事件和来自所有其它对象和集合构成的集合。例如,AcadApplication对象有一集合为Documents(即Documents集合),在其中对应有一个或多个Document对象。每一Document对象有它自己的对象、集合、属性和方法以及其它东西。

match ordering {
    Relaxed | Release | Acquire | AcqRel | SeqCst => { /* ... */ }
    // OK; if more variants are added, nothing will break.
    _ => { /* logic */ }
}

你可向下层浏览对象模型进入下一层次的对象和集合,也可以向上层浏览父对象和集合。模型非常强大,应用程序可直接访问和操作环境来执行几乎无限的任务。它同时保持每一事物都整齐有序,在开发软件解决方案时能提供有效的帮助。

有关该#[non_exhaustive]属性的更多详细信息,可参见稳定性报告

集合和词典
集合是在相同父窗口中一组相似的对象。该容器有一个独特的名字,在大多数情况下,将提供自己的方法来访问所包含的对象。词典是一种特殊类型的集合,它允许你扩展你自己的词典。Visual LISP并没有过多提供用于创建或处理集合的方法。它允许你遍历它、修改项目、添加或删除条目。词典允许你添加自己的词典并写入数据,你可遍历它、添加、修改和删除其条目,同样,你也可以添加 、修改和删除词典本身。

Macro and attribute 的改进

在AutoCAD中的一些公共的集合有Documents(文档)、Layers(图层)、Dimension Styles(标注样式)、Linetypes(线型)、 Blocks(块)等等。

在AutoCAD中的一些公共的词典有PageSetups (页面设置)、Layouts (布局)(它同样也做为词典保存),还有Express Tools中的个别组件,如WipeOuts (遮罩)。Xrecord对象也保存在词典内。

例如,用户可以编写以下类型:Foo = expand_to_type!(bar); 其中 expand_to_type 将是一个 procedural macro。

属性、方法和事件
属性只是描述关联于对象或集合的特性。例如它可包含名称、高度、宽度、旋转角度、比例缩放、颜色、图层、线型等等。属性根据不同的对象的类型而有所区别,但有些属性对所有对象和集合是通用的。集合和词典通常提供了Count和Name属性,还有Item和Add方法。只有词典提供了Delete方法,而你却不能通过Visual LISP删除集合。

方法是对象提供的用于访问或编辑专用特性或针对对象本身执行特别动作的内置函数。常见的方法有Rotate(旋转)、Erase(删除)、Copy(复制)、Scale(比例缩放)和Offset(偏移)。你可能注意到,这些看起来就像AutoCAD编辑命令。嗯,在本质上是这样的,但略有不同。

包括有bang!() macros, 例如:

然而一般的AutoCAD编辑命令,必须在每步执行中验证对象。而方法是由主对象提供的,因此,只能由每一对象单独提供支持的方法。晕了吧?

macro_rules! make_item { ($name:ident) => { fn $name(); } }

extern {
    make_item!(alpha);
    make_item!(beta);
}

另一种方式,OFFSET(偏移)命令可以在任何时间使用,但如果你试图偏移一个文本对象, AutoCAD就会出现出错信息。然而,文本对象本身提供的各种方法,如Copy(复制)、Rotate(旋转)、Scale(比例缩放)和Move(移动),但没有Offset(偏移)。所以你可以从对象“调用”各种方法,却需要确定它对使用的对象是有效的。

Procedural macro attributes on items in extern { ... } blocks 现在也被支持:

事件是对象或集合由各种可以被检测到的或可以响应的活动所产生动作。当事件与这些事件的反应相结合使用时,就称为事件驱动编程。AutoCAD提供了一个被称之为反应器的强大的事件反应工具集,它使你可在绘图环境中发送触发器来响应各种事件。例如,当对象在活动的图形中被删除时,您可以创建一个反应器响应一个Erase事件。这只是一个事件和反应器的例子。

extern "C" {
    // Let's assume that this expands to `fn foo();`.
    #[my_identity_macro]
    fn foo();
}

属性关联
有一点很重要,就是你决不需要知道哪些属性适用于哪些对象和集合。有两个特别的函数对于保证您的代码能够在运行时妥善处理属性和方法的执行:(vlax-property-available-p)和(vlax-method-applicable-p)。这两个函数只是两个Visual LISP判断函数,它提供布尔测试判断条件是真或假(LISP术语为non-nil或nil)。

这些函数的语法如下:
(vlax-property-available-p object property) 
(vlax-method-applicable-p object method) 

目前,函数式(mac!())和属性(#[mac])macros 都可以生成macro_rules!项目。

属性是跟与之关联的对象的类型相关。例如,一个Circle(圆)对象有一个Diameter(直径)属性,但Line (线)对象就没有。根据不同的对象类型,属性也各不一样,下面的代码在拾取CIRCLE(圆)图元时会出错:

(if (setq ent (entsel "n选择对象以获取其属性: "))
  (progn
    (setq obj (vlax-ename->vla-object (car ent)))
    (princ
      (strcat "n长度: " (vla-get-Length obj))
    )
  )
)

也就是说,以下内容现在有效:

但是,如果在取对象的属性前先检验该属性是否有效时,代码的执行就能正常,如下面的例子所示:

macro_rules! accept_meta { ($m:meta) => {} }
accept_meta!( my::path );
accept_meta!( my::path = "lit" );
accept_meta!( my::path ( a b c ) );
accept_meta!( my::path [ a b c ] );
accept_meta!( my::path { a b c } );

(if (setq ent (entsel "n选择对象以获取其属性: "))
  (progn
    (setq obj (vlax-ename->vla-object (car ent)))
    (if (vlax-property-available-p obj 'Length)
      (princ
        (strcat "n长度: " (vla-get-Length obj))
      )
      (princ "n对象没有 LENGTH 属性…")
    )
  )
)

标准库中增加的 const fn

不幸的是,没有直接的方法获取给定对象的所有属性的列表来用于遍历编程。不过,你还是可以获取列表信息的,这样对你的帮助也不算小。

此版本中,以下函数成为const fn

要查询在给定的对象上有什么属性和方法,可在该对象上使用(vlax-dump-object)函数。函数的语法是(vlax-dump-object object show-methods), show-methods参数可以是nil或非nil。如果是非nil,就显示对象支持的方法,否则就不显示方法。

图 3-2 – Documents集合的属性和方法 

增加到标准库的函数

图3-2显示了Documents集合对象的属性和方法。你可看到输出的第一行显示了带有一个描述的内部对象参照(IAcadDocuments),并且列出了可用的属性和方法。

以下函数和宏已经稳定:

提示! 以下的命令定义对显示所选图元的属性和方法会有用处。它没有提供出错处理,但是它仍是个有用的小工具。

(defun C:DUMP (/ ent obj)
  (while (setq ent (entsel "n选择图元以获取对象数据: "))
    (setq obj (vlax-ename->vla-object (car ent)))
    (vlax-dump-object obj T)
    (vlax-release-object obj)
  )
  (princ)
)

详情可查看更新说明:

提示! 属性后面的括号(RO)代表该属性为只读,在这里,所有的属性均为只读,方法后面括号内的数字表示了每一方法所需要的参数数目。

https://blog.rust-lang.org/2019/12/19/Rust-1.40.0.html

要访问属性,可使用(vla-get-xxx)函数,或更通用的(vlax-get-Property)函数,两者都可以。第一个函数的语法是(vla-get-xxx object),其中XXX是属性名。在使用(vlax-get-property)函数时,语法为(vlax-get-property object propertyname),其中propertyname可以是双引号字符串或单引号字符串。

(文/开源中国)    

(vlax-get-property object property) 或 
(vla-get-property object) 或 
(vlax-get object property) 

返回值是分配给该对象的指定名称属性的值,如果该对象的指定属性不存在,就会产生错误。举例来说,如果你对Line(直线)图元查询“Diameter”(直径)属性,就会产生错误。

参数: 
Object – 一个 vla-对象 
Property – 与对象相关的有效属性。 

示例: 
(vlax-get-property objLine “Length”) 
(vlax-get-property objLine ‘Length) 
(vla-get-Length objLine) 

所有这些语句都将完成同样的事情。

属性的名称并不要求区分大小写,但本书的例子都将首字母大写,以示清晰。总的来说,你会发现以上前两项都很好用,但也有情况是要求用到后两项。特别是涉及到象Microsoft  Excel 或 Word这样的外部应用程序。第四种方式 vlax-get 是R14版遗留下来的,只为了向后兼容而已。

使用方法
通过图3-2的例子,你可看到Documents集合对象支持4种方法:Add、Close、Item和Open。Item方法需要使用一个参数(也就是在图3-2中显示在方法后面的那个(1)),它是由集合所引出的文档的索引和命名。

通常Item方法有一个有趣的特性,那就是它可以接受字符串或整数值参数。当给的是整数参数时,它就简单地返回集合的第(nth)条目,0为第一条目。当给的是一个字符串值时,它尝试通过它的名称属性来取得条目。Item (名称)方法不分大小写,这是相当有用的,它不需要先对字符串进行大小写转换就可以接受名称。

提示! 如果你熟悉Visual Basic或VBA并经常使用默认方法和默认属性,那你要注意,这种特性在Visual LISP是不存在的。例如,在Visual Basic中,通过以下任意一种途径来访问Item方法都是可行的:
Object.Item(12) 或 Object(12) 或 Object(“Name”) 

这是因为在VB或VBA中,Item方法是大多数对象的默认方法。Visual LISP并不支持这种特性,它需要在每次使用时都阐明所有属性和方法。例如:

(vlax-invoke-method documents “Item” 12) 会有效果… 
(vla-item documents “Drawing1.dwg”) 会有效果… 
(vlax-invoke-method documents 12) 不会有效果。 

使用图3-2的例子,Item方法可通过以下任何方法使用:

(vla-Item documents 1) 
(vla-Item documents “Drawing1.dwg”) 
(vlax-invoke-method documents “Item” 1) 
(vlax-invoke-method documents ‘Item “Drawing1.dwg”) 

(vlax-invoke-method object method [arguments]...) 或 
(vla-method object arguments) 或 
(vlax-invoke object method [arguments] ...) 

调用关联于对象的方法并为方法提供任何所需的参数。如果成功,将返回结果。如果对象没有提供对应的方法,将会产生ActiveX错误。例如,在Text(文本)图元中调用“Offset”(偏移)方法,将会产生ActiveX错误。

参数: 
Object – 一个vla-对象 
Method – 由对象显露的方法 
Arguments – 支持该方法的任何所需的参数 

示例: 
(vlax-invoke-method objLine “Move” point1 point2) 
(vla-Move objLine point1 point2) 
(vlax-invoke objLine “Move” point1 point2) 

所有这些示例做的事情都一样。对于大部分AutoCAD对象来说都是可行的,但对于由TypeLib接口或外部应用程序或ActiveX组件输入创建的对象则不都可行。你可以将第一种方法用于外部应用程序对象,而可以将第二种方法用于内部对象。第三种方法是为了R14的向后兼容。

提示! 当你选择对属性和方法用Get或Put中的任意一种形式时,你会发现用较长的形式(如vlax-put-property)比用较短的形式(如vla-put-color)更灵活。原因是通过从函数中分离出属性名称,你就可以定义函数和循环语句,它们可以接受一个属性列表及相关的数值,例如...

(defun MapPropertyList (object proplist)
  (foreach propset proplist
    (if (vlax-property-available-p object (car propset))
      (vlax-put-property object (car propset) (cadr propset))
    )
  )
)

当试图在方法上使用这种方式时要特别小心,因为方法的参数列表会随着对象和方法的变化而变化。有一些方法不需要参数,而另一些则随长度变化。

数据类型

数据类型是用于描述给定对象或属性可包含的数值的类型。数据类型的例子有整数、双精度、货币、日期、字符串等等。在AutoLISP已经享受“类型无关”多年后,Visual LISP也同样可以,但不单只这样。在AutoCAD里,你可以尽可能地通过AutoLISP来保留这种类型无关,但当涉及其它应用程序时,例如Microsoft Excel,你将不可避免而且明智地用到数据类型。

类型无关并非免费午餐,其代价是效率低下。当你提前声明了数据类型,你就通知编译器调动仅仅够用的资源来匹配预定数据。例如,存储整数类型的数据远比存储“长”数据值要低得多。当你未使用数据类型时,所有数据都会自动分配尽可能长的数据类型,以确保能匹配所有可能出现的数据。其结果导致应用程序消耗的资源远比它的需求大得多,无论是从初始加载的尺寸或者其运行时间资源的分配。

这就是为什么程序开发的语言中,象C++、Java,甚至是Visual Basic通常都是比较快的(相比使用自由类型语言编程的同样功能)。他们确保能提前精简处理以保证运行时有更快的性能。AutoLISP并不这样做,因此是相对慢的处理语言。而Visual LISP相对会快一些,但只有你利用其新的特性,让其最尽可能发挥。

常量和枚举
常量是特殊的数据类型。正如其名,它的值是不可改变的。有时被称为静态。通常来说,常量是由程序语言或由托管应用程序本身提供,作为一种方便的手段。例如,acByLayer常量在属性值中可以被256代替。名称值比整数值更好明白也更好记。例如,以下两个语句在功能上说是一致的。

(vla-put-color object acByLayer) 
(vla-put-color object 256) 

枚举是常量的逻辑组合,它用来确定常量值范围。例如你可以用颜色值1、2、3、4、5,但也可以将之常量化为acRed、acYellow、acGreen、acCyan、acBlue、acMagenta和acWhite,这样更清晰。这种类型的相关常量值范围即称为枚举。参看附录A为标准的AutoCAD枚举清单。

提示!并不是所有的ActiveX枚举都在Visual LISP中有提供。例如,标准的Decimal(十进制)或Short(短)数据类型并没有镜像为vlax-vbDecimal或vlax-vbShort。请参阅第六章学习更多关于数据类型的信息。

变体和安全数组
在上节的数据类型中,有提到对自由类型声明使用最大的可用配额,例如在AutoLISP中的(setq)语句。事实上,是分配了一个变体数据类型,变体包括所有的数据类型,它提供了足够的资源空间去包含任何其它数据类型,可以是数值、日期、字符串或其它。事实上,变体数据类型是ActiveX产生出来的。但这个概念更具有一般性,在ActiveX之前已经存在很长的时间。

事实上Visual LISP包含了将所有ActiveX数据转换为带有指示包含哪类类型的区分符的变体。感觉有点乱,但事实很简单。该容器是一个包含有货币数据类型值的变体,当你分配给对象一个新值时,你必须提供区分符以使得这个数据能够正确储存。这是必须要做的,尤其是你在AutoCAD和其它应用程序(如Microsoft Excel)之间传递数值时。

除了发送数值外,你还可以从变体值中查询嵌套的数据类型,也可将值正确的转换成为相关的LISP数据类型。例如,你可查询变体对象值是否包含了双精度值。然后你可以在LISP中将这个数值读取为实数(REAL)数据类型。Visual LISP提供了大量的函数用于在LISP环境中创建、读取、修改变体量数据值。

安全数组相当于AutoLISP中的LIST对象。主要的区别在于它是静态的。意思就是说他不可能根据他们能存储的数字的多少来加长或改变项数。这将避免了在试图分配或取得超出数组长度的元素时产生的不必要的错误。这就是它们之所以被称为“安全”的原因。任何传递到ActiveX的LIST结构必须先转换成安全数组。任何从ActiveX对象获得的LIST面向对象的数据都必须通过象(car)、(nth)、(accoc)、(mapcar)、(member)等的LISP函数转换成LIST数据类型。Visual LISP为创造操作、阅读安全数组数据值提供了大量的功能。

更多关于变体和安全数组的内容,请参阅第六章。

命名空间
命名空间是分配用于运行过程的虚拟空间,它与空间中的其它资源相互配合。但它有时也可以和其它命名空间中的其它过程相联系。可以把命名空间想象成卧室。你的应用程序就如在卧室工作的人,也即在特定命名空间的过程。其它的应用程序就如在邻近卧室(命名空间)工作的一样,这两个人可以相互独立和隔离的,但他们也可以互相传递数据以便沟通。这就是命名空间工作的原理。

使用命名空间的好处在于可以在特定的命名空间中的过程与另外命名空间相独立的,这样可以在它们之间不相互破坏(如在争夺保存资源时)。它们也可直接通过它们的命名空间加载和卸载过程。换句话说,这有点象拆掉房子里一间卧室,而房子就如模块化形式建的一样。移去一个房间不会影响到其它房间或过程的生存。

可能使用命名空间最大的短处是会导致在操作系统或其宿主应用程序上的一些开销。为了管理好给定的命名空间,它必须有自己的内存地址范围和指针分配。这需要消耗额外的资源去跟踪和控制命名空间,这反过来又在必要时提供了直接的方法去访问它、卸载它或暂停它。

AutoCAD提供了在Visual LISP内部对命名空间的内部管理方法,同样在ObjectARX和VBA中也可以。这是Visual LISP比AutoLISP功能强大改进的又一表现。实际上每个打开的文档都是其自己的命名空间(如果你不是在单文件模式下工作的话),当在一个图形中设置一个变量而又试图在另一个图形中读取它时,这个影响就会看到。是有办法可以实现在这些图形中传递变量的,我们第十章将会谈到。

接口和类型库
接口是用于连接其它ActiveX过程或构件的对象模型的手段。当你想利用其它应用程序的特定的属性、常量或方法时,你首先要定义一个接口去加载那个目标应用的对象模型。例如,有时你想通过Visual LISP来利用Excel自己的工具直接用Microsoft Excel来保存一些AutoCAD信息到电子表格文件。这就需要定义接口,然后就可使用类型库

要使用类型库,就必须将其加载到内存,并确定接口指针已经定义。Visual LISP提供了一毓函数用于加载和配置类型库接口。

(vlax-import-type-library 
  :tlb-filename name string 
  :methods-prefix    string 
  :properties-prefix string 
  :constants-prefix  string 

输入类型库参照到当前命名空间中。

参数: 
:tlb-filename string – (字符串) 为类型库文件的路径和文件名 
:methods-prefix string – (字符串) 为任意前缀字符串标识符 
:properties-prefix string – (字符串) 为任意前缀字符串标识符
:constants-prefix string – (字符串) 为任意前缀字符串标识符

示例: 
(vlax-import-type-library
  :tlb-filename             "c:\myfiles\typelibs\tlfile.tlb"
  :methods-prefix           "dsxm-"
  :properties-prefix        "dsxp-"
  :constants-prefix         "dsxc-"
)

该示例输入了由tlfile.tlb文件定义的外部应用程序或控件的类型库接口。其它的参数定义了类型库接口所暴露的方法、属性和常量的前缀。

如果该类型库提供了一个名为AddNumbers的方法,在我们的Visual LISP代码中它将被识别为dsxm-AddNumbers。有趣的是,一旦你已经输入了类型库并且程序运行成功后,Visual LISP将识别这些外部应用程序中的所有已定义的属性、方法、常量,并将其做为内置的LISP函数把它们的代码变蓝。这也是Visual LISP IDE可以帮你编程及提高你及时发现错误的能力的另一原因。

图3-3 类型库接口

类型库仅仅是一个接口,它暴露了提供者的所有对象模型成员供给其它应用程序查询用。当加载类型库时,它立刻定义并识别相关应用程序提供者的所有公开暴露的属性、常量和方法给应用程序使用者使用。

在图3-3中,Excel类型库被加载以做便Visual LISP可以连接到Excel对象模型并使用其暴露的工具。这样可以节省你很多时间和麻烦,因为它在Excel中建立了直接的访问工具,这样就可以做你想需要的而不必尝试在Visual LISP中重复另起炉灶。下面的例子显示了它是如何使用的。

例如,当通过从Visual LISP中调用,将一个定值做为参数提供给Excel函数,你可以用常量枚举名来代替实际所代表的数值以使得你的编码清晰易懂。这也省去了你需要浏览Excel中的所有枚举并把它们翻译成Visual LISP的时间。如果Excel提供了一个如put-cellcolor的常量时,那就可以在Visual LISP中直接使用它。

Visual LISP需要类型库信息来确定对象的方法、属性和常量是否存在。有些对象不含有任何类型库信息,例如AcadDocument对象。

(vlax-typeinfo-available-p object) 

如果对象的类型库信息存在,返回T。如果不存在,则返回nil。

参数:
Object – 一个vla-对象。 

(defun Excel-Get-Cell (rng row column)
  (vlax-variant-value
    (msxl-get-item
      (msxl-get-cells rng)
      (vlax-make-variant row)
      (vlax-make-variant column)
    )
  )
)
(defun Excel-Put-CellColor (row col intcol / rng)
  (setq rng (Excel-Get-Cell (msxl-get-ActiveSheet xlapp) row col))
  (msxl-put-colorindex (msxl-get-interior rng) intcol)
)

上例中定义的第二个函数提供了一个在Visual LISP中对一个Excel工作表中的给定单元填色的方法。这可以通过使用Excel中已暴露接口的方法,这是通过首先加载Excel类型库实现的。这个以上显示的类型库项带有一个 msxl-前缀。

当你调用了一个类型库接口,其中涉及到的函数随后会被VLISP编辑器语法引擎识别到。当你正确地输入了它们后,它们就会改变颜色以显示它们确实被从外面的类型库接口中被视为一个有效的函数而显示出来。这是一个很有用的编码练习的基础:语法感知。

提示!类型库有很多种形式,他们通常是.TLB文件。但也可以是.OLB、.DLL,甚至是.EXE。值得一提的是Microsoft Office 97 和 2000版通常用.TLB文件,而OFFICE XP自己使用.EXE文件来对类型库提供对其它应用的接口定义。请翻阅与你准备使用外部应用程序或服务相关的参考资料以了解它是如何暴露共ActiveX类型库信息的内容。

尽管变体和安全数组数据类型这个话题在本书的前几节已经讲过,但在Visual LISP的ActiveX领域中它们具有相当重要的位置,值得我们用一整章的篇幅来专门讲解。我们将首先简要了解他们是什么,然后开始探索如何通过Visual LISP函数来使用它们。

正如我们前面提到的,变体是一种数据类型,它被设计成能包含所有任何其它数据的通用容器。他们消耗最多的内存并可处理所有的数据类型,因为他们会根据资源需求取最大内存。

象C/C++、Visual Basic以及Delphi这类语言会提供声明语句去提前通知编译器确定每个变量将包含什么数据类型。这不仅保证了精简的资源需求,同时也允许编辑期间进行错误检查,以防止运行时的问题。

Visual LISP变体函数
(vlax-make-variant [value] [type]) 
使用给定的值或符号赋值来创建一个变体对象

参数:
Value - 要指定给变体的值。如果省略该参数,变体将创建为 vlax-vbEmpty 类型(未初始化)。
Type – 变体的数据类型,如果省略该参数,变体将根据最接近的ActiveX数据类型来确定LISP数据类型(看下表)。

示例:
(vlax-make-variant) 或 (vlax-make-variant nil) 
创建一个未初始化(vlax-vbEmpty)变体。

(vlax-make-variant 10 :vlax-vbInteger) 
创建一个整型(vlax-vbInteger) 变体,其值为10。

(vlax-make-variant “vlisp example”) 
创建一个字符串类型(vlax-vbString)变体,其值为“vlisp example”。

(setq dblarray (vlax-make-safearray vlax-vbDouble ‘(0 . 3))) 
(vlax-make-variant dblarray :vlax-vbArray) 
创建一个包含双精度值安全数组的变体。

提示!ActiveX数据类型中的十进制和短型在Visual LISP中并不支持,但当从外部资源读取值时,你可以用(vlax-variant-type)来指定其类型。将这些类型的数据发送到外部资源时,你需要使用数字表示法来替代(vlax-vbDecimal)和(vlax-vbShort),因为在Visual LISP中它们并未作为枚举提供。例如,十进制数据类型的枚举值为14。

变体数据类型
如果你不为变体构造器指定数据类型又会怎样?Visual LISP将会使用默认映射尝试将它转换到一个适当的数据类型。表6-1显示了从LISP到变体的数据类型的默认映射。

  LISP 数据类型      分配的变体默认数据类型   
  nil      vlax-vbEmpty   
  :vlax-null      vlax-vbNull   
  INT (integer)      vlax-vbLong   
  REAL (float)      vlax-vbDouble   
  STR (string)      vlax-vbString   
  VLA-OBJECT      vlax-vbObject   
  :vlax-true or :vlax-false      vlax-vbBoolean   
  VARIANT      与初始值的类型相同   
  SafeArray      vlax-vbArray   
  N/A      vlax-vbShort   
  N/A      vlax-vbDecimal   
  N/A      vlax-vbDate   

表 6-1 – Visual LISP默认LISP->变体数据类型映射 

(vlax-variant-type variant) 
返回变体的数据类型,如果variant不是变体,则返回错误信息。返回值是数据类型的枚举值(查看附录A的数据类型枚举值)。

参数:
Variant – 变体。

示例:
(setq vartest (vlax-make-variant 6 vlax-vbInteger)) 
(vlax-variant-type vartest) 
返回 2 (整数类型) 

(setq vartest (vlax-make-variant “dog” vlax-vbString)) 
(vlax-variant-type vartest) 
返回 8 (字符串类型) 

(vlax-variant-value symbol) 
返回包含于变体符号中值。如果symbol不包含变体数据类型。则返回错误信息。

参数:
Symbol – 包含变体值的符号。

示例:
(setq vartest (vlax-make-variant “testvalue” vlax-vbString)) 
(vlax-variant-value vartest) 
将“testvalue”值作为字符串结果返回。

(setq sa (vlax-make-safearray vlax-vbDouble ‘(0 . 2))) 
(setq vartest (vlax-make-variant sa vlax-vbDouble)) 
(vlax-variant-value vartest) 
返回#<safearray...>值,它是vla-object类型。

(vlax-safearray->list (vlax-variant-value vartest)) 
返回结果是(0.0 0.0 0.0)列表值。

(vlax-variant-change-type symbol type) 
更改变体的数据类型。

参数:
Symbol - 变体值
Type - 要转换到的数据类型数字或枚举

示例:
(setq vartest (vlax-make-variant 5 vlax-vbInteger)) 
(setq vartest (vlax-variant-change-type vartest vlax-vbString)) 
转换vartest为字符串类型(vlax-vbString)变体,它将致使通过(vlax-variant-value)返回的值为“5”。

(vlax-make-safearray type dim1 [dim2] …) 
创建一个安全数组,其数据类型为type,维度范围dim1,以此类推,可以指定额外的维度。不管什么原因,如果操作失败,语句将返回nil。 

参数:
Type – 数据类型(整数或枚举)
Dim1 – 第一维数组(一维数组)
Dim2 – (可选)第二维维数(二维数组)等。

示例:
(setq sa (vlax-make-safearray vlax-vbDouble ‘(0 . 2))) 
创建一个双精度的单维数组,它可容纳三个不同的元素(0,1,2)。

(setq sa (vlax-make-safearray vlax-vbString ‘(0 . 1) ‘(1 . 3))) 
创建一个字符型二维数组,第一维包含两个元素,由索引0开始。第二给包含三个元素,由索引1开始。

提示!要填充安全数组,可使用(vlax-safearray-fill)还是(vlax-safearray-put-element)来填充,至于使用哪个填充,可根据用户是否需要一次只指定一个元素或一次性指定所有元素来决定。

(vlax-safearray->list symbol) 
如果symbol包含安全数组,元素将转换为LISP LIST数据类型被返回。如果symbol不包含安全数组,会产生错误。你需要将对该函数的调用封装到错误捕获内以使这个错误能得到适当处理。

参数:
Symbol – 包含安全数组的符号

(vlax-safearray-type symbol) 
如果symbol包含安全数组,元素的数据类型将以枚举结果返回(整数值)。这将可匹配整数或枚举结果(查看附录X有关数据类型枚举)。如果symbol不包含安全数组,将产生错误。

参数:
Symbol – 包含安全数组的符号

示例:
(setq sa (vlax-make-safearray vlax-vbdouble ‘(0 . 3))) 
(vlax-safearray-type sa) 
返回5(双精度),相当于vlax-vbDouble

(vlax-safearray-fill safearray ‘element-values) 
指定值给安全数组内的多个元素。如果提供的参数不是一个数组,将返回ActiveX错误。你需要将对该函数的调用封装到错误捕获内以使这个错误能得到适当处理。

参数:
Safearray        安全数组类型的对象
Element-values        一个存储于数组中的值列表,你可以指定与数组中元素一样多的值。如果你指定的数值少于元素个数,剩余的元素保留他们当前值或为空。对于多维数组,element-values必须是列表的列表,每一列表对应数组的一维。

示例:
创建一个双精度值的单维数组:
_$ (setq myarray (vlax-make-safearray vlax-vbdouble '(0 . 2))) 
#<safearray...> 
使用vlax-safearray-fill来填充数组元素值:
_$ (vlax-safearray-fill myarray '(1 2 3)) 
#<safearray...> 
列出数组包含的值以校验元素值:
_$ (vlax-safearray->list myarray) 
(1.0 2.0 3.0) 

(vlax-safearray-get-element safearray element [element...]) 
返回安全数组中指定元素的值,element的值是整数,表示在该数组中要取得的索引位置。如果safearray参数非安全数组对象,将产生ActiveX错误。你需要将对该函数的调用封装到错误捕获内以使这个错误能得到适当处理。

参数:
Safearray –安全数组类型的对象
Element – 整数,指要取得的索引位置

示例:
_$ (setq sa (vlax-make-safearray vlax-vbString '(1 . 2) '(1 . 2) ))
#<safearray...> 
使用vlax-safearray-put-element来填充数组:
_$ (vlax-safearray-put-element sa 1 1 "A") 
"a" 
_$ (vlax-safearray-put-element sa 1 2 "B") 
"b" 
_$ (vlax-safearray-put-element sa 2 1 "C") 
"c" 
_$ (vlax-safearray-put-element sa 2 2 "D") 
"d" 
使用vlax-safearray-get-element来检索数组第一维的第二元素:
_$ (vlax-safearray-get-element sa 1 1) 
”A” 
_$ (vlax-safearray-get-element a 2 2) 
”D" 

(vlax-safearray-put-element safearray element [element...] value) 
在安全数组中指定新值给单个元素。如果safearray参数不是安全数组对象,将产生ActiveX错误。如果提供的元素值不能与数组中的数据类型相匹配,将返回ActiveX错误。你需要将对该函数的调用封装到错误捕获内以使这个错误能得到适当处理。

参数:
Safearray         安全数组类型的对象
Element         指向你将指定值的元素所在位置的系列索引值。对于一维数组,指定一个索引值,对于二维数组,指定两个索引值,以此类推。
Value         指定给每个元素的值。在数组中要指定不同的值给数组中个别的元素,要分开调用独立的值来对应不同的元素位置。

示例:
_$ (setq sa (vlax-make-safearray vlax-vbString '(1 . 2) '(1 . 2) )) 
#<safearray...> 
使用vlax-safearray-put-element来填充数组:
_$ (vlax-safearray-put-element sa 1 1 "A") 
"A" 
_$ (vlax-safearray-put-element sa 1 2 "B") 
"B" 
_$ (vlax-safearray-put-element sa 2 1 "C") 
"C" 
_$ (vlax-safearray-put-element sa 2 2 "D") 
"D" 
你也可以用vlax-safearray-fill函数来填充数组值,以下的函数调用能和三个vlax-safearray-put-element调用一样完成同样的任务:
(vlax-safearray-fill sa '(("A" "B") ("C" "D"))) 

(vlax-safearray-get-dim safearray) 
返回给定安全数组中的维数(数组维数的数字)。如果给定的参数不是数组,将返回ActiveX错误。你需要将对该函数的调用封装到错误捕获内以使这个错误能得到适当处理。

参数:
Safearray 安全数组类型的对象

示例:
_$ (setq myarray (vlax-make-safearray vlax-vbinteger '(2 . 5))) 
#<safearray...> 
_$ (vlax-safearray-get-dim myarray) 

(vlax-safearray-get-l-bound safearray dim) 
返回指定数组的维数下界(整数值)。如果给定的参数不是数组,将返回ActiveX错误。你需要将对该函数的调用封装到错误捕获内以使这个错误能得到适当处理。

参数:
Safearray – 安全数组类型的对象
Dim – 整数,数组的第几维,第一维为1

示例:
以下示例是求以下所定义的安全数组的下界值:
(vlax-make-safearray vlax-vbString '(1 . 2) '(0 . 1) )) 
取得数组第一维的起始索引值(下界):
_$ (vlax-safearray-get-l-bound tmatrix 1) 1 

(vlax-safearray-get-u-bound safearray dim) 
返回指定数组维度的上界(整数值)。如果指定的参数不是数组,将返回ActiveX错误。你需要将对该函数的调用封装到错误捕获内以使这个错误能得到适当处理。

本文由美高梅游戏平台网站发布于操作系统,转载请注明出处:Rust 1.40.0 发布

关键词: