Common Lisp:如何覆盖插槽访问器?

and*_*i-n 2 overriding accessor common-lisp clos

我想控制插槽中保存值的方式以及读取插槽时返回的内容.这是我的班级定义:

(defclass object ()
  ((name :accessor name-access
         :initform 'noname
         :initarg :name)
   (value :accessor value-access
      :initform 10
      :initarg :value)))
Run Code Online (Sandbox Code Playgroud)

我这样创建对象:

(setf obj1 (make-instance 'object))
Run Code Online (Sandbox Code Playgroud)

这是我获取插槽值的方式name:

(name-access obj1)
Run Code Online (Sandbox Code Playgroud)

我如何设置一个新值:

(setf (name-access obj1) 'realname)
Run Code Online (Sandbox Code Playgroud)

覆盖此访问器函数(或方法)的正确方法是什么,以便能够对对象(写入时)进行一些更改并控制返回的值?

谢谢.

jki*_*ski 5

您可以手动定义获取和设置插槽的方法:

(defclass foo ()
  ((name :initform 'noname
         :initarg :name)))

(defgeneric name-access (foo)
  (:method ((foo foo))
    (format t "~&Getting name.~%")
    (slot-value foo 'name)))

(defgeneric (setf name-access) (name foo)
  (:method (name (foo foo))
    (format t "~&Setting a new name.~%")
    (setf (slot-value foo 'name) name)))

(defparameter *foo* (make-instance 'foo))
(name-access *foo*)
; Getting name.
;=> NONAME

(setf (name-access *foo*) 'some-name)
; Setting a new name.
;=> SOME-NAME

(name-access *foo*)
; Getting name.
;=> SOME-NAME
Run Code Online (Sandbox Code Playgroud)

实用Common Lisp一书在第17章介绍了这些.你应该读一下.


Rai*_*wig 5

您可以扩展由DEFCLASS以下定义的访问器方法:

CL-USER 66 > (defclass object ()
               ((name :accessor name-access
                      :initform 'noname
                      :initarg :name)
                (value :accessor value-access
                       :initform 10
                       :initarg :value)))
#<STANDARD-CLASS OBJECT 4220014953>
Run Code Online (Sandbox Code Playgroud)

写作,使用:before方法:

CL-USER 67 > (defmethod (setf name-access) :before (new-value (o1 object))
               (print "hi"))
#<STANDARD-METHOD (SETF NAME-ACCESS) (:BEFORE) (T OBJECT) 40202283BB>
Run Code Online (Sandbox Code Playgroud)

阅读,用一种:around方法:

CL-USER 68 > (defmethod name-access :around ((o1 object))
               (let ((name (call-next-method)))
                 (values name (length (symbol-name name)))))
#<STANDARD-METHOD NAME-ACCESS (:AROUND) (OBJECT) 4020061213>
Run Code Online (Sandbox Code Playgroud)

例:

CL-USER 69 > (let ((o1 (make-instance 'object)))
               (setf (name-access o1) 'foobar)
               (name-access o1))

"hi"     ; side effect
FOOBAR   ; return value 1
6        ; return value 2
Run Code Online (Sandbox Code Playgroud)