注册 登录
编程论坛 VFP论坛

我也来谈谈我对结构的理解

sam_jiang 发布于 2022-11-06 15:56, 1818 次点击
个人觉得结构就是一个格式化的字符串,它和类有些类似,可以继承,但没有封装方法。

如果把结构看做类,那么结构的成员就相当于类的属性。

下面是point结构的一个定义
Type POINTAPI
    x As Long
    y As Long
End Type

通过point.x来访问结构的成员x,这跟我们的类不是很像吗?

我们来构建一个point类吧。
第一步,构建一个通用类struct
**************************************************
*-- Class:        struct (d:\documents\visual foxpro 项目\myclass.vcx)
*-- ParentClass:  custom
*-- BaseClass:    custom
*-- Time Stamp:   11/03/22 11:51:11 AM
*****************设置一个通用类*******************
DEFINE CLASS struct AS custom

    *-- 用以储存或设置结构体数据
    struct = ""
    Name = "struct"


    *-- 设置结构体
    PROCEDURE set
    ENDPROC

ENDDEFINE
*
*-- EndDefine: struct
**************************************************

第二步,构建point类

*****************************************************
*-- Class:        _point (d:\documents\visual foxpro 项目\myclass.vcx)
*-- ParentClass:  struct (d:\documents\visual foxpro 项目\myclass.vcx)
*-- BaseClass:    custom
*-- Time Stamp:   11/03/22 08:59:09 PM
********point类和系统同名,所以加了条下划线**********
DEFINE CLASS _point AS struct


    *-- x坐标
    x = .F.

    *-- Y坐标
    y = .F.


*!*    PROCEDURE struct_assign
*!*        LPARAMETERS vNewVal
*!*        *To do: Modify this routine for the Assign method
*!*        THIS.struct = m.vNewVal
*!*        this.x=CTOBIN(SUBSTR(m.vnewval,1,4),"4rs")
*!*        this.y=CTOBIN(SUBSTR(m.vnewval,5,4),"4rs")
*!*    ENDPROC


    PROCEDURE Init
        this.struct=REPLICATE(CHR(0),8) &&初始化结构体
    ENDPROC


    PROCEDURE set
            PARAMETERS xorcstruct,y

            DO case
                CASE PARAMETERS()=1 AND VARTYPE(xorcstruct)=="C" && 注意这里的xorcstruct不是数字,而是8字节字符,我并没有对字符串的有效性进行检查。
                this.x=CTOBIN(SUBSTR(xorcstruct,1,4),"4rs")
                this.y=CTOBIN(SUBSTR(xorcstruct,5,4),"4rs")
                this.struct=xorcstruct
            CASE PARAMETERS()=2
                this.x=xorcstruct
                this.y=y
                this.struct=BINTOC(xorcstruct,"4rs")+BINTOC(y,"4rs")
            ENDCASE
        *!*PARAMETERS x,y
        *!*this.struct=BINTOC(x,"4rs")+BINTOC(y,"4rs")

    ENDPROC


ENDDEFINE
*
*-- EndDefine: _point
**************************************************

第三步,实例化
*!* pointstructtest.prg
CLEAR
DECLARE integer GetCursorPos IN WIN32API string@
SET CLASSLIB TO "D:\Documents\Visual FoxPro 项目\myclass.VCX"
opoint=CREATEOBJECT("_point")
cstruct=opoint.struct
getcursorpos(@cstruct)
opoint.struct=cstruct
opoint.set(cstruct)
?opoint.x
?opoint.y
release opoint
clear dlls

************end**************

不知道这样的理解对大家有没有帮助???

[此贴子已经被作者于2022-11-6 16:01编辑过]

19 回复
#2
csyx2022-11-07 12:19
最大区别是结构的各成员有严格的存储次序,类属性无此要求

[此贴子已经被作者于2022-11-7 12:21编辑过]

#3
schtg2022-11-07 15:37
向各位学习!
#4
吹水佬2022-11-07 16:19
回复 楼主 sam_jiang
可以说语句表达形式类似(写法有点像),实质就不是一回事。
结构是不同类型成员紧密有序集合的数据块(同一空间)。
结构还有特别的地方,就拿DBF说,每条记录就是一个结构块,声明一个相同结构的东东,套在记录的上就可以直接按结构成员读写每条记录字段的数据。就好象拿个饼印模在面团上面压盖,就可以压出一个一个一样的饼。这饼印就是结构,饼就是每条记录。


#5
sam_jiang2022-11-07 16:30
再来一个rectangle结构

类定义如下:
**************************************************
*-- Class:        rectangle (d:\documents\visual foxpro 项目\myclass.vcx)
*-- ParentClass:  struct (d:\documents\visual foxpro 项目\myclass.vcx)
*-- BaseClass:    custom
*-- Time Stamp:   11/07/22 04:18:08 PM
*
DEFINE CLASS rectangle AS struct

    *-- 左上角x坐标
    x = .F.

    *-- 左上角y坐标
    y = .F.

    PROCEDURE set
        PARAMETERS xorcstruct,y,nwidth,nheight

        DO case
            CASE PARAMETERS()=1 AND VARTYPE(xorcstruct)=="C" && 注意这里的xorcstruct不是数字,而是16字节字符,我并没有对字符串的有效性进行检查。
            this.x=CTOBIN(SUBSTR(xorcstruct,1,4),"4rs")
            this.y=CTOBIN(SUBSTR(xorcstruct,5,4),"4rs")
            this.width=CTOBIN(SUBSTR(xorcstruct,9,4),"4rs")
            this.height=CTOBIN(SUBSTR(xorcstruct,13,4),"4rs")
            this.struct=xorcstruct
        CASE PARAMETERS()=4
            this.x=xorcstruct
            this.y=y
            this.Width=nwidth
            this.Height=nheight
            this.struct=BINTOC(xorcstruct,"4rs")+BINTOC(y,"4rs")+BINTOC(nwidth,"4rs")+BINTOC(nheight,"4rs")
        OTHERWISE
            RETURN .f.
        ENDCASE
    ENDPROC

ENDDEFINE
*
*-- EndDefine: rectangle
**************************************************

获得_screen的rect

*getscreenrect.prg
CLEAR
DECLARE inte GetWindowRect IN WIN32API inte,string@
cstr=REPLICATE(CHR(0),16)
getwindowrect(_screen.HWnd,@cstr)
SET CLASSLIB TO myclass
orect=CREATEOBJECT("rectangle")
orect.set(cstr)
?"获得orect的端点值"
?orect.x
?orect.y
?orect.width
?orect.height
?"获得_screen的端点值,看是否相同"
?_screen.Left
?_screen.Top
?_screen.Width
?_screen.Height
RELEASE orect
CLEAR DLLS
SET CLASSLIB TO
#6
厨师王德榜2022-11-07 17:21
这样的帖子多多益善!
#7
sam_jiang2022-11-07 20:50
回复 4楼 吹水佬
你这个比喻比较形象,一条记录就相当于一个结构体。
#8
sam_jiang2022-11-07 20:56
回复 6楼 厨师王德榜
抛砖引玉,在各位大佬面前班门弄斧了。。。
#9
zhken2022-11-07 21:23
多多益善
#10
zhken2022-11-07 21:36
多多益善
#11
iswith2022-11-07 22:51
以下是引用吹水佬在2022-11-7 16:19:12的发言:

可以说语句表达形式类似(写法有点像),实质就不是一回事。
结构是不同类型成员紧密有序集合的数据块(同一空间)。
结构还有特别的地方,就拿DBF说,每条记录就是一个结构块,声明一个相同结构的东东,套在记录的上就可以直接按结构成员读写每条记录字段的数据。就好象拿个饼印模在面团上面压盖,就可以压出一个一个一样的饼。这饼印就是结构,饼就是每条记录。

描述比较准确。。。。有序同一空间
#12
sam_jiang2022-11-08 09:15
我们把结构封装为类,目的是省却foxer去记复杂的api函数声明,以及涩晦难懂的结构申明,所以还应该对我的point类进行改造。

改造后的point类如下:

**************************************************
*-- Class:        _point (d:\documents\visual foxpro 项目\myclass.vcx)
*-- ParentClass:  struct (d:\documents\visual foxpro 项目\myclass.vcx)
*-- BaseClass:    custom
*-- Time Stamp:   11/08/22 09:02:13 AM
*
DEFINE CLASS _point AS struct


    *-- x坐标
    x = .F.

    *-- Y坐标
    y = .F.


    PROCEDURE struct_assign
        LPARAMETERS vNewVal
        *To do: Modify this routine for the Assign method
        *!*    THIS.struct = m.vNewVal
        *!*    this.x=CTOBIN(SUBSTR(m.vnewval,1,4),"4rs")
        *!*    this.y=CTOBIN(SUBSTR(m.vnewval,5,4),"4rs")
    ENDPROC


    PROCEDURE set
        PARAMETERS xorcstruct,y

        DO case
            CASE PARAMETERS()=1 AND VARTYPE(xorcstruct)=="C" && 注意这里的xorcstruct不是数字,而是8字节字符,我并没有对字符串的有效性进行检查。
            this.x=CTOBIN(SUBSTR(xorcstruct,1,4),"4rs")
            this.y=CTOBIN(SUBSTR(xorcstruct,5,4),"4rs")
            this.struct=xorcstruct
        CASE PARAMETERS()=2
            this.x=xorcstruct
            this.y=y
            this.struct=BINTOC(xorcstruct)+BINTOC(y)
        OTHERWISE
            RETURN .f.
        ENDCASE
        *!*    PARAMETERS x,y
        *!*    this.struct=BINTOC(x,"4rs")+BINTOC(y,"4rs")

    ENDPROC


    PROCEDURE Init
        DECLARE integer GetCursorPos IN WIN32API string@
    ENDPROC


    PROCEDURE Destroy
        CLEAR DLLS
    ENDPROC


    *-- 获取当前鼠标所在点的位置,成功返回.t.
    PROCEDURE get
        cstruct=opoint.struct
        getcursorpos(@cstruct)
        this.set(cstruct)
    ENDPROC


ENDDEFINE
*
*-- EndDefine: _point
**************************************************


这样用户就不需要申明api函数了,直接用就可以了。

***pointtest.prg***
CLEAR
SET CLASSLIB TO "D:\Documents\Visual FoxPro 项目\myclass.VCX"
opoint=CREATEOBJECT("_point")
opoint.get()
?opoint.x
?opoint.y
release opoint
**************

当需要将一个确定的点以结构体的形式当参数传给其他函数yourfunc,可以这样用:

opoint.set(200,500) &&设置坐标点(200,500)
yourfunc(opoint.struct) &&传给你的函数






[此贴子已经被作者于2022-11-8 09:18编辑过]

#13
sam_jiang2022-11-08 09:50
改造后的rectangle类如下:

**************************************************
*-- Class:        rectangle (d:\documents\visual foxpro 项目\myclass.vcx)
*-- ParentClass:  struct (d:\documents\visual foxpro 项目\myclass.vcx)
*-- BaseClass:    custom
*-- Time Stamp:   11/08/22 09:41:07 AM
*
DEFINE CLASS rectangle AS struct

    *-- 左上角x坐标
    x = .F.

    *-- 左上角y坐标
    y = .F.

    *-- 获得form或window窗体位置的矩形结构,成功返回.t.
    PROCEDURE get
        PARAMETERS Oform

        IF !VARTYPE(oform)=="O"
            RETURN .f.
        ENDIF

        IF oform.class=="Form" OR oform=_vfp
            cstruct=this.struct
            getwindowrect(oform.hwnd,@cstruct)
            this.set(cstruct)
            RETURN .t.
        ELSE
            RETURN .f.
        ENDIF

    ENDPROC


    PROCEDURE Destroy
        CLEAR DLLS
    ENDPROC

    PROCEDURE Init
        DECLARE integer GetWindowRect IN WIN32API integer nhwnd, string @rect
    ENDPROC

    PROCEDURE set
        PARAMETERS xorcstruct,y,nwidth,nheight

        DO case
            CASE PARAMETERS()=1 AND VARTYPE(xorcstruct)=="C" && 注意这里的xorcstruct不是数字,而是16字节字符,我并没有对字符串的有效性进行检查。
            this.x=CTOBIN(SUBSTR(xorcstruct,1,4),"4rs")
            this.y=CTOBIN(SUBSTR(xorcstruct,5,4),"4rs")
            this.width=CTOBIN(SUBSTR(xorcstruct,9,4),"4rs")
            this.height=CTOBIN(SUBSTR(xorcstruct,13,4),"4rs")
            this.struct=xorcstruct
        CASE PARAMETERS()=4
            this.x=xorcstruct
            this.y=y
            this.Width=nwidth
            this.Height=nheight
            this.struct=BINTOC(xorcstruct,"4rs")+BINTOC(y,"4rs")+BINTOC(nwidth,"4rs")+BINTOC(nheight,"4rs")
        OTHERWISE
            RETURN .f.
        ENDCASE
    ENDPROC

ENDDEFINE
*
*-- EndDefine: rectangle
**************************************************

实例:
***getscreenrect()
CLEAR
SET CLASSLIB TO myclass
orect=CREATEOBJECT("rectangle")
orect.get(_screen) &&只支持自定义form,_screen和_vfp
?"获得orect的端点值"
?orect.x
?orect.y
?orect.width
?orect.height
?"获得_screen的端点值,看是否相同"
?_screen.Left
?_screen.Top
?_screen.Width
?_screen.Height
RELEASE orect
SET CLASSLIB TO  
******end******

#14
吹水佬2022-11-08 10:09
觉得这样来封装类有点浪费,没有充分发挥到类的作用。
类最大的特点是“继承”,子承父业。
这样封装只是一个类的“特例”,个人觉得还不如封装成调用API的函数来得简单清晰明了。
另:释构时的 CLEAR DLLS 杀伤力有点大,如果其他过程也有调用API就有可能出现“找不到xxxxx.prg”。
#15
sam_jiang2022-11-08 20:09
回复 14楼 吹水佬
是的,我欠缺考虑,rectangle类的destroy事件应该改为 clear dlls "GetWindowRect",_point类的destroy事件应该改为 clear dlls "GetCursorPos" 。这样就不会误杀了。
#16
csyx2022-11-09 04:39
以下是引用sam_jiang在2022-11-8 20:09:21的发言:

rectangle类的destroy事件应该改为 clear dlls "GetWindowRect",_point类的destroy事件应该改为 clear dlls "GetCursorPos" 。这样就不会误杀了。

一样是误杀,你不能假设人家就不声明和使用这两个api函数
另一方面,这样构造的结构类完全没必要,常用的win32api结构少说也有几百种,难不成使用一个就构造一个?设计一个通用的就得,只提供几个必要的方法即可 Define(用来定义结构成员),Parse(用于分解字符串块到各成员 or 类属性),toString(拼合各属性到一字符串)
如果使用者连结构的成员都不了解,也就没资格使用结构类了,直接封装win32api函数给他调用更简单


[此贴子已经被作者于2022-11-9 04:44编辑过]

#17
sam_jiang2022-11-10 02:00
虽说是小概率事件,但确实有那个可能,依旧误杀。不过这个问题可以解决,申明API函数前先查一下是否此函数已经申明,已申明则说明有其他程序也在使用,就不要释放。

鉴于每个结构都是独一无二的,没办法,只能为每一个结构创建一个类,创建通用类怕是不行的。

大多数狐友都不是很了解结构,而实际上却有使用结构的需求。他们不可能像你们那样了解它,那么写这样的结构类就可以帮到他们!

事实上,系统里的GDIPLUS类,以及狐友提供的System.drawing类,就是把结构以及API函数封装起来,供使用者调用,而不必关心怎么申明API函数,以及如何使用结构。
#18
asdf_1230002022-11-11 13:18
学习
#19
sam_jiang2022-11-11 13:20
根据csyx的意见,我再改造一下point类!

init事件:
*******************************************************************************
ndeclared=ADLLS(cdlls)
IF ndeclared>0
    IF ASCAN(cdlls,"GetCursorPos")>0
        this.declared=.t.
    ENDIF        
    RELEASE cdlls
ELSE     
    DECLARE integer GetCursorPos IN WIN32API string@
    this.declared=.f.
ENDIF

RELEASE ndeclared

*******************************************************************************

destory事件:

IF this.declared=.f.
    CLEAR DLLS "GetCursorPos"
ENDIF

rectangle类的改造如下:

init事件:
*******************************************************************************        
ndeclared=ADLLS(cdlls)
IF ndeclared>0
    IF ASCAN(cdlls,"GetWindowRect")>0
        this.declared=.t.
   
    ENDIF        
    RELEASE cdlls
ELSE     
    DECLARE integer GetWindowRect IN WIN32API integer, string@
    this.declared=.f.
ENDIF
RELEASE ndeclared   

destroy事件:
*******************************************************************************   
IF this.declared=.f.
    CLEAR DLLS "GetWindowRect"
ENDIF

*********************************The End***************************************     
#20
schtg2022-11-12 06:01
回复 19楼 sam_jiang
谢谢!
1