




已阅读5页,还剩20页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Delphi 的RTTI机制浅探 2004.1.20目录 RTTI 简介 类(class) 和 VMT 的关系 类(class)、类的类(class of class)、类变量(class variable) 的关系 TObject.ClassType 和 TObject.ClassInfo is 和 as 运算符的原理 TTypeInfo RTTI 信息的结构 获取类(class)的属性(property)信息 获取方法(method)的类型信息 获取有序类型(ordinal)、集合(set)类型的 RTTI 信息 获取其它数据类型的 RTTI 信息本文排版格式为:正文由窗口自动换行;所有代码以 80 字符为边界;中英文字符以空格符分隔。(作者保留对本文的所有权利,未经作者同意请勿在在任何公共媒体转载。)正文 RTTI 简介RTTI(Run-Time Type Information) 翻译过来的名称是“运行期类型信息”,也就是说可以在运行期获得数据类型或类(class)的信息。这个 RTTI 到底有什么用处,我现在也说不清楚。我是在阅读 Delphi 持续机制的代码中发现了很多 RTTI 的运用,只好先把 RTTI 学习一遍。下面是我的学习笔记。如果你发现了错误请告诉我。谢谢!Delphi 的 RTTI 主要分为类(class)的 RTTI 和一般数据类型的 RTTI,下面从类(class)开始。 类(class) 和 VMT 的关系一个类(class),从编译器的角度来看就是一个指向 VMT 的指针(在后文用 VMTptr 表示)。在类的 VMTptr 的负地址方向存储了一些类信息的指针,这些指针的值和指针所指的内容在编译后就确定了。比如 VMTptr - 44 的内容是指向类名称(ClassName)的指针。不过一般不使用数值来访问这些类信息,而是通过 System.pas 中定义的以 vmt 开头的常量,如 vtmClassName、vmtParent 等来访问。类的方法有两种:对象级别的方法和类级别的方法。两者的 Self 指针意义是不同的。在对象级别的方法中 Self 指向对象地址空间,因此可以用它来访问对象的成员函数;在类级别的方法中 Self 指向类的 VMT,因此只能用它来访问 VMT 信息,而不能访问对象的成员字段。 类(class)、类的类(class of class)、类变量(class variable) 的关系上面说到类(class) 就是 VMTptr。在 Delphi 中还可以用 class of 关键字定义类的类,并且可以使用类的类定义类变量。从语法上理解这三者的关键并不难,把类当成普通的数据类型来考虑就可以了。在编译器级别上表现如何呢?为了简化讨论,我们使用 TObject、TClass 和 TMyClass 来代表上面说的三种类型:type TClass = class of TObject;var TMyClass: TClass; MyObject: TObject;begin TMyClass := TObject; MyObject := TObject.Create; MyObject := TClass.Create; MyObject := TMyClass.Create;end; 在上面的例子中,三个 TObject 对象都被成功地创建了。编译器的实现是:TObject 是一个 VMTPtr 常量。TClass 也是一个 VMTptr 常量,它的值就是 TObject。TMyClass 是一个 VMTptr 变量,它被赋值为 TObject。TObject.Create 与 TClass.Create 的汇编代码完全相同。但 TClass 不仅缺省代表一个类,而且还(主要)代表了类的类型,可以用它来定义类变量,实现一些类级别的操作。 TObject.ClassType 和 TObject.ClassInfofunction TObject.ClassType: TClass;begin Pointer(Result) := PPointer(Self);end; TObject.ClassType 是对象级别的方法,Self 的值是指向对象内存空间的指针,对象内存空间的前 4 个字节是类的 VMTptr。因此这个函数的返回值就是类的 VMTptr。class function TObject.ClassInfo: Pointer;begin Result := PPointer(Integer(Self) + vmtTypeInfo);end; TObject.ClassInfo 使用 class 关键字定义,因此是一个类级别的方法。该方法中的 Self 指针就是 VMTptr。所以这个函数的返回值是 VMTptr 负方向的 vmtTypeInfo 的内容。TObject.ClassInfo 返回的 Pointer 指针,实际上是指向类的 RTTI 结构的指针。但是不能访问 TObject.ClassInfo 指向的内容(TObject.ClassInfo 返回值是 0),因为 Delphi 只在 TPersistent 类及 TPersistent 的后继类中产生 RTTI 信息。(从编译器的角度来看,这是在 TPersistent 类的声明之前使用 $M+ 指示字的结果。)TObject 还定义了一些获取类 RTTI 信息的函数,列举在下,就不一一分析了: TObject.ClassName: ShortString; 类的名称 TObject.ClassParent: TClass; 对象的父类 TObject.InheritsFrom: Boolean; 是否继承自某类 TObject.InstanceSize: Longint; 对象实例的大小 is 和 as 运算符的原理我们知道可以在运行期使用 is 关键字判断一个对象是否属于某个类,可以使用 as 关键字把某个对象安全地转换为某个类。在编译器的层次上,is 和 as 的操作是由 System.pas 中两个函数完成的。 System.pas function _IsClass(Child: TObject; Parent: TClass): Boolean;begin Result := (Child nil) and Child.InheritsFrom(Parent);end; _IsClass 很简单,它使用 TObject 的 InheritsForm 函数判断该对象是否是从某个类或它的父类中继承下来的。每个类的 VMT 中都有一项 vmtParent 指针,指向该类的父类的 VMT。TObject.InheritsFrom 实际上是通过递归判断父类 VMT 指针是否等于自己的 VMT 指针来判断是否是从该类继承的。 System.pas class function TObject.InheritsFrom(AClass: TClass): Boolean;var ClassPtr: TClass;begin ClassPtr := Self; while (ClassPtr nil) and (ClassPtr AClass) do ClassPtr := PPointer(Integer(ClassPtr) + vmtParent); Result := ClassPtr = AClass;end; as 操作符实际上是由 System.pas 中的 _AsClass 函数完成的。它简单地调用 is 操作符判断对象是否属于某个类,如果不是就触发异常。虽然 _AsClass 返回值为 TObject 类型,但编译器会自动把返回的对象改变为 Parent 类,否则返回的对象没有办法使用 TObject 之外的方法和数据。 System.pas function _AsClass(Child: TObject; Parent: TClass): TObject;begin Result := Child; if not (Child is Parent) then Error(reInvalidCast); / loses return addressend; TTypeInfo RTTI 信息的结构RTTI 信息的结构定义在 TypInfo.pas 中: TTypeInfo = record / TTypeInfo 是 RTTI 信息的结构 Kind: TTypeKind; / RTTI 信息的数据类型 Name: ShortString; / 数据类型的名称 TypeData: TTypeData / RTTI 的内容 end; TTypeInfo 就是 RTTI 信息的结构。TObject.ClassInfo 返回指向存放 class TTypeInfo 信息的指针。Kind 是枚举类型,它表示 RTTI 结构中所包含数据类型。Name 是数据类型的名称。注意,最后一个字段 TypeData 被注释掉了,这说明该处的结构内容根据不同的数据类型有所不同。TTypeKind 枚举定义了可以使用 RTTI 信息的数据类型,它几乎包含了所有的 Delphi 数据类型,其中包括 tkClass。 TTypeKind = (tkUnknown, tkInteger, tkChar, tkEnumeration, tkFloat, tkString, tkSet, tkClass, tkMethod, tkWChar, tkLString, tkWString, tkVariant, tkArray, tkRecord, tkInterface, tkInt64, tkDynArray); TTypeData 是个巨大的记录类型,在此不再列出,后文会根据需要列出该记录的内容。 获取类(class)的属性(property)信息这一段是 RTTI 中最复杂的部分,努力把本段吃透,后面的内容都是非常简单的。下面是一个获取类的属性的例子:procedure GetClassProperties(AClass: TClass; AStrings: TStrings);var PropCount, I: SmallInt; PropList: PPropList; PropStr: string;begin PropCount := GetTypeData(AClass.ClassInfo).PropCount; GetPropList(AClass.ClassInfo, PropList); for I := 0 to PropCount - 1 do begin case PropListI.PropType.Kind of tkClass : PropStr := Class ; tkMethod : PropStr := Method; tkSet : PropStr := Set ; tkEnumeration: PropStr := Enum ; else PropStr := Field ; end; PropStr := PropStr + PropListI.Name; PropStr := PropStr + : + PropListI.PropType.Name; AStrings.Add(PropStr); end; FreeMem(PropList);end; 你可以在表单上放置一个 TListBox ,然后执行以下语句观察执行结果: GetClassProperties(TForm1, ListBox1.Items); 该函数先使用 GetTypeData 函数获得类的属性数量。GetTypeData 是 TypInfo.pas 中的一个函数,它的功能是返回 TTypeInfo 的 TypeData 数据的指针: TypInfo.pas function GetTypeData(TypeInfo: PTypeInfo): PTypeData; assembler; class 的 TTypeData 结构如下: TTypeData = packed record case TTypeKind of tkClass: ( ClassType: TClass; / 类 (VMTptr) ParentInfo: PPTypeInfo; / 父类的 RTTI 指针 PropCount: SmallInt; / 属性数量 UnitName: ShortStringBase; / 单元的名称 PropData: TPropData); / 属性的详细信息 end;其中的 PropData 又是一个大小可变的字段。TPropData 的定义如下: TPropData = packed record PropCount: Word; / 属性数量 PropList: record end; / 占位符,真正的意义在下一行 PropList: array1.PropCount of TPropInfo end;每个属性信息在内存中的结构就是 TPropInfo,它的定义如下: PPropInfo = TPropInfo; TPropInfo = packed record PropType: PPTypeInfo; / 属性类型信息指针的指针 GetProc: Pointer; / 属性的 Get 方法指针 SetProc: Pointer; / 属性的 Set 方法指针 StoredProc: Pointer; / 属性的 StoredProc 指针 Index: Integer; / 属性的 Index 值 Default: Longint; / 属性的 Default 值 NameIndex: SmallInt; / 属性的名称索引(以 0 开始计数) Name: ShortString; / 属性的名称 end;为了方便访问属性信息,TypInfo.pas 中还定义了指向 TPropInfo 数组的指针: PPropList = TPropList; TPropList = array0.16379 of PPropInfo; 我们可以使用 GetPropList 获得所有属性信息的指针数组,数组用完以后要记得用 FreeMem 把数组的内存清除。 TypInfo.pas function GetPropList(TypeInfo: PTypeInfo; out PropList: PPropList): Integer; GetPropList 传入类的 TTypeInfo 指针和 TPropList 的指针,它为 PropList 分配一块内存后把该内存填充为指向 TPropInfo 的指针数组,最后返回属性的数量。上面的例子演示了如何获得类的所有属性信息,也可以根据属性的名称单独获得属性信息: TypInfo.pas function GetPropInfo(TypeInfo: PTypeInfo; const PropName: string): PPropInfo; GetPropInfo 根据类的 RTTI 指针和属性的名称字符串,返回属性的信息 TPropInfo 的指针。如果没有找到该属性,则返回 nil。GetPropInfo 很容易使用,举个例子: ShowMessage(GetPropInfo(TForm, Name).PropType.Name);这句调用显示了 TForm 类的 Name 属性的类型名称:TComponentName。 获取方法(method)的类型信息所谓方法就是以 of object 关键字声明的函数指针,下面的函数可以显示一个方法的类型信息:procedure GetMethodTypeInfo(ATypeInfo: PTypeInfo; AStrings: TStrings);type PParamData = TParamData; TParamData = record / 函数参数的数据结构 Flags: TParamFlags; / 参数传递规则 ParamName: ShortString; / 参数的名称 TypeName: ShortString; / 参数的类型名称 end; function GetParamFlagsName(AParamFlags: TParamFlags): string; var I: Integer; begin Result := ; for I := Integer(pfVar) to Integer(pfOut) do begin if I = Integer(pfAddress) then Continue; if TParamFlag(I) in AParamFlags then Result := Result + + GetEnumName(TypeInfo(TParamFlag), I); end; end;var MethodTypeData: PTypeData; ParamData: PParamData; TypeStr: PShortString; I: Integer;begin MethodTypeData := GetTypeData(ATypeInfo); AStrings.Add(-); AStrings.Add(Method Name: + ATypeInfo.Name); AStrings.Add(Method Kind: + GetEnumName(TypeInfo(TMethodKind), Integer(MethodTypeData.MethodKind); AStrings.Add(Params Count: + IntToStr(MethodTypeData.ParamCount); AStrings.Add(Params List:); ParamData := PParamData(MethodTypeData.ParamList); for I := 1 to MethodTypeData.ParamCount do begin TypeStr := Pointer(Integer(ParamData.ParamName) + Length(ParamData.ParamName) + 1); AStrings.Add(Format( %s %s: %s,GetParamFlagsName(ParamData.Flags), ParamData.ParamName, TypeStr); ParamData := PParamData(Integer(ParamData) + SizeOf(TParamFlags) + Length(ParamData.ParamName) + Length(TypeStr) + 2); end; if MethodTypeData.MethodKind = mkFunction then AStrings.Add(Result Value: + PShortString(ParamData);end;作为实验,在表单上放置一个 TListBox,然后执行以下代码,观察执行结果:type TMyMethod = function(A: array of Char; var B: TObject): Integer of object;procedure TForm1.FormCreate(Sender: TObject);begin GetMethodTypeInfo(TypeInfo(TMyMethod), ListBox1.Items); GetMethodTypeInfo(TypeInfo(TMouseEvent), ListBox1.Items); GetMethodTypeInfo(TypeInfo(TKeyPressEvent), ListBox1.Items); GetMethodTypeInfo(TypeInfo(TMouseWheelEvent), ListBox1.Items);end; 由于获取方法的类型信息比较复杂,我尽量压缩代码也还是有这么长,让我们看看它的实现原理。GetMethodTypeInfo 的第一个参数是 PTypeInfo 类型,表示方法的类型信息地址。第二个参数是一个字符串列表,可以使用任何实现 TStrings 操作的对象。我们可以使用 System.pas 中的 TypeInfo 函数获得任何类型的 RTTI 信息指针。TypeInfo 函数像 SizeOf 一样,是内置于编译器中的。GetMethodTypeInfo 还用到了 TypInfo.pas 中的 GetEnumName 函数。这个函数通过枚举类型的整数值得到枚举类型的名称。function GetEnumName(TypeInfo: PTypeInfo; Value: Integer): string;与获取类(class)的属性信息类似,方法的类型信息也在 TTypeData 结构中 TTypeData = packed record case TTypeKind of tkMethod: ( MethodKind: TMethodKind; / 方法指针的类型 ParamCount: Byte; / 参数数量 ParamList: array0.1023 of Char / 参数详细信息,见下行注释 ParamList: array1.ParamCount of record Flags: TParamFlags; / 参数传递规则 ParamName: ShortString; / 参数的名称 TypeName: ShortString; / 参数的类型 end; ResultType: ShortString); / 返回值的名称 end;TMethodKind 是方法的类型,定义如下: TMethodKind = (mkProcedure, mkFunction, mkConstructor, mkDestructor, mkClassProcedure, mkClassFunction, Obsolete mkSafeProcedure, mkSafeFunction);TParamsFlags 是参数传递的规则,定义如下: TParamFlag = (pfVar, pfConst, pfArray, pfAddress, pfReference, pfOut); TParamFlags = set of TParamFlag; 由于 ParamName 和 TypeName 是变长字符串,不能直接取用该字段的值,而应该使用指针步进的方法,取出参数信息,所以上面的代码显得比较长。 获取有序类型(ordinal)、集合(set)类型的 RTTI 信息讨论完了属性和方法的 RTTI 信息之后再来看其它数据类型的 RTTI 就简单多了。所有获取 RTTI 的原理都是通过 GetTypeData 函数得到 TTypeData 的指针,再通过 TTypeInfo.TypeKind 来解析 TTypeData。任何数据类型的 TTypeInfo 指针可以通过 TypeInfo 函数获得。有序类型的 TTypeData 定义如下:TTypeData = packed record tkInteger, tkChar, tkEnumeration, tkSet, tkWChar: ( OrdType: TOrdType; / 有序数值类型 case TTypeKind of case TTypeKind of tkInteger, tkChar, tkEnumeration, tkWChar: ( MinValue: Longint; / 类型的最小值 MaxValue: Longint; / 类型的最大值 case TTypeKind of tkInteger, tkChar, tkWChar: (); tkEnumeration: ( BaseType: PPTypeInfo;/ 指针的指针,它指向枚举的 PTypeInfo NameList: ShortStringBase; / 枚举的名称字符串(不能直接取用) EnumUnitName: ShortStringBase);/所在的单元名称(不能直接取用) tkSet: ( CompType: PPTypeInfo);/ 指向集合基类 RTTI 指针的指针end;下面是一个获取有序类型和集合类型的 RTTI 信息的函数:procedure GetOrdTypeInfo(ATypeInfo: PTypeInfo; AStrings: TStrings);var OrdTypeData: PTypeData; I: Integer;begin OrdTypeData := GetTypeData(ATypeInfo); AStrings.Add(-); AStrings.Add(Type Name: + ATypeInfo.Name); AStrings.Add(Type Kind: + GetEnumName(TypeInfo(TTypeKind), Integer(ATypeInfo.Kind); AStrings.Add(Data Type: + GetEnumName(TypeInfo(TOrdType), Integer(OrdTypeData.OrdType); if ATypeInfo.Kind tkSet then begin AStrings.Add(Min Value: + IntToStr(OrdTypeData.MinValue); AStrings.Add(Max Value: + IntToStr(OrdTypeData.MaxValue); end; if ATypeInfo.Kind = tkSet then GetOrdTypeInfo(OrdTypeData.CompType, AStrings); if ATypeInfo.Kind = tkEnumeration then for I := OrdTypeData.MinValue to OrdTypeData.MaxValue do AStrings.Add(Format( Value %d: %s, I, GetEnumName(ATypeInfo, I);end;在表单上放置一个 TListBox,运行以下代码查看结果:type TMyEnum = (EnumA, EnumB, EnumC);procedure TForm1.FormCreate(Sender: TObject);begin GetOrdTypeInfo(TypeInfo(Char), ListBox1.Items); GetOrdTypeInfo(TypeInfo(Integer), ListBox1.Items); GetOrdTypeInfo(TypeInfo(TFormBorderStyle), ListBox1.Items); GetOrdTypeInfo(TypeInfo(TBorderIcons), ListBox1.Items); GetOrdTypeInfo(TypeInfo(TMyEnum), ListBox1.Items);end;(如果枚举元素没有按缺省的 0 基准定义,那么将不能产生 RTTI 信息,为什么?) 获取其它数据类型的 RTTI 信息上面讨论了几个典型的 RTTI 信息的运行,其它的数据类型的 RTTI 信息的获取方法与上面类似。由于这些操作更加简单,就不一一讨论。下面概述其它类型的 RTTI 信息的情况:LongString、WideString 和 Variant 没有 RTTI 信息;ShortString 只有 MaxLength 信息;浮点数类型只有 FloatType: TFloatType 信息; TFloatType = (ftSingle, ftDouble, ftExtended, ftComp, ftCurr);Int64 只有最大值和最小值信息(也是 64 位整数表示);Interface 和动态数组不太熟悉,就不作介绍了。 结束来自:小雨哥, 时间:2004-1-21 13:23:00, ID:2420817他们说,执行下面代码看看:implementationuses Unit2;$R *.DFMprocedure TForm1.btnShowClick(Sender: TObject);beginif FindClass(TForm2) nil then ShowMessage(有了 :);end;procedure TForm1.btnRegisterClick(Sender: TObject);beginRegisterClass(TForm2);end;先不要按 btnRegister 这个按钮,直接按 btnShow 发现提示错误。 来自:savetime, 时间:2004-1-21 15:37:00, ID:2420892to 小雨歌,你说的是问题还是提醒我 FindClass 也属于 RTTI ?本文中的 RTTI 是最简单的,我准备在续文写下更详细的 RTTI 内容。来自:savetime, 时间:2004-1-23 1:23:00, ID:2421471大家新年好,Delphi 的RTTI机制浅探(续) 写完了,敬请指正:/delphibbs/dispq.asp?lid=2421470 来自:beta, 时间:2004-1-27 18:28:00, ID:2423886希望把您写的几篇笔记转贴到 还望恩准:D你要是不说 no 我就当答应了啊:DDelphi 的RTTI机制浅探(续) 2004.1.22本文是Delphi 的RTTI机制浅探的续篇,上篇地址在:/delphibbs/dispq.asp?lid=2420610本文上篇基本上是 RTTI 入门介绍,续篇介绍了所有 TypInfo.pas 中的函数,附加了 Classes.pas、Graphics.pas、Controls.pas 中的几个 RTTI 相关函数。对于关键函数的代码提供汇编注释。希望本文覆盖了 Delphi 中 80% 的 RTTI 函数。时间仓促,错误难免,敬请批评指正。本文排版格式为:正文由窗口自动换行;所有代码以 80 字符为边界;中英文字符以空格符分隔。(作者保留对本文的所有权利,未经作者同意请勿在在任何公共媒体转载。)目 录 GetTypeData 函数 GetPropInfo 函数 FindPropInfo 函数 GetPropInfos 函数 SortPropList 函数 GetPropList 函数- GetObjectPropClass 函数 PropType / PropIsType 函数 IsPublishedProp 函数 IsStoredProp 函数 FreeAndNilProperties 函数 SetToString / StringToSet 函数 GetEnumName / GetEnumValue / GetEnumNameValue 函数- GetOrdProp 函数详解 SetOrdProp 函数 GetEnumProp / SetEnumProp 函数 GetSetProp / SetSetProp 函数 GetObjectProp / SetObjectProp 函数 GetStrProp / SetStrProp 函数 GetFloatProp / SetFloatProp 函数 GetPropValue / SetPropValue 函数 TPublishableVariantType class- RegisterClass / FindClass 系列函数 (Classes.pas) IdentToInt / IntToIdent 系列函数 (Classes.pas)正 文 GetTypeData 函数GetTypeData 函数根据 TTypeInfo 指针获得 TTypeData 的地址。function GetTypeData(TypeInfo: PTypeInfo): PTypeData;asm XOR EDX,EDX ; EDX 清零 MOV DL,EAX.TTypeInfo.Name.Byte0 ; 获得 Name 字符串长度 LEA EAX,EAX.TTypeInfo.NameEDX+1 ; 获得 TTypeData 的地址end; GetPropInfo 函数GetPropInfo 函数用于获得属性的 RTTI 指针 PPropInfo。它有四种重载形式,后面三种重载的实现都是调用第一种形式。AKinds 参数用于限制属性的类型,如果得到的 PPropInfo 不属于指定的类型,则返回 nil。 function GetPropInfo(TypeInfo: PTypeInfo; const PropName: string): PPropInfo; function GetPropInfo(Instance: TObject; const PropName: string; AKinds: TTypeKinds = ): PPropInfo; function GetPropInfo(AClass: TClass; const PropName: string; AKinds: TTypeKinds = ): PPropInfo; function GetPropInfo(TypeInfo: PTypeInfo; const PropName: string; AKinds: TTypeKinds): PPropInfo; FindPropInfo 函数FindPropInfo 函数根据属性名称获得属性的 RTTI 指针,它只是在 GetPropInfo 函数的基础上加上了错误检查功能,如果没有属性 RTTI 信息,则触发 EPropertyError 异常。function FindPropInfo(Instance: TObject; const PropName: string): PPropInfo;function FindPropInfo(AClass: TClass; const PropName: string): PPropInfo; GetPropInfos 函数GetPropInfos 函数的功能是把一个类(class)所有属性 RTTI 指针 PPropInfo 填充至传入的参数 PPropList 数组中。注意:这个函数不负责分配该数组的内容,使用前必须根据属性的数量分配足够的空间。该数组结束后必须清除分配的内容。 procedure GetPropInfos(TypeInfo: PTypeInfo; PropList: PPropList);注:使用 GetPropList 实现相同的功能更方便。 SortPropList 函数SortPropList 可以对 GetPropInfos 函数填充的属性信息指针数组按属性名称排序。 procedure SortPropList(PropList: PPropList; PropCount: Integer);在 VCL 中 SortPropList 只被 GetPropList 函数使用。 GetPropList 函数GetPropList 函数同 GetPropInfos 一样,填充 PPropList 数组。GetPropList 实际上是调用 GetPropInfos 进行填充工作,最后返回已填充的属性的数量。 function GetPropList(TypeInfo: PTypeInfo; TypeKinds: TTypeKinds; PropList: PPropList; SortList: Boolean): Integer; function GetPropList(TypeInfo: PTypeInfo; out PropList: PPropL
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 工业的地域形成课件
- 子宫平滑肌瘤课件
- 子宫内膜癌课件
- 工业电器安全培训课件
- 娱乐主播教学课件
- 娱乐中科普健康知识课件
- 威海食品安全知识培训课件
- 工业机器人教学课件
- 威尼的小艇课件
- 2024年利辛县招聘城市社区专职工作人员考试真题
- 2024版2025秋贵州黔教版综合实践活动五年级上册全册教案教学设计
- 转作风重实干课件
- 村干部饮水安全培训总结课件
- 安全生产治本攻坚三年行动半年工作总结
- 单招备考科学方案
- 医美咨询培训课件
- 海船船员适任 评估规范(2024)轮机专业
- DB50-T 1463.2-2023 牛羊布鲁氏菌病防控技术规范 第2部分:人员防护
- NoSQL数据库应用与实践 课件 第1-6章 认识NoSQL - 增删改查
- 20世纪宋史研究:主要趋势、热点领域与未来展望
- 2025年度餐饮店知识产权保护与合伙人合同
评论
0/150
提交评论