当前位置: 首页>>代码示例 >>用法及示例精选 >>正文


R ReferenceClasses 具有按引用处理的字段的对象(OOP 样式)


R语言 ReferenceClasses 位于 methods 包(package)。

说明

此处说明的软件允许包定义以 “OOP” 语言(例如 Java 和 C++)风格运行的引用类。此 OOP 模型不同于 S4(和 S3)类和方法实现的函数模型,其中方法是为泛型函数定义的。参考类的方法是类定义中的“encapsulated”。

使用引用类中的对象进行计算调用它们的方法并提取或设置它们的字段,使用`$`运算符在R。字段和方法计算可能会修改对象。与通常的函数式编程模型相比,引用对象的所有计算都会看到修改R.

在包的源代码中调用 setRefClass 定义该类并返回一个生成器对象。对生成器的 $methods() 方法的后续调用将为该类定义方法。与函数类一样,如果该类是从包中导出的,则在加载包时它将可用。

方法有R函数。在通常的实现中,它们直接通过名称引用类的字段和其他方法。请参阅“Writing Reference Methods” 部分。

与函数类一样,引用类可以通过 setRefClasscontains= 参数从其他引用类继承。字段和方法将被继承,除非新类覆盖方法定义。请参阅“Inheritance” 部分。

用法

setRefClass(Class, fields = , contains = , methods =,
     where =, inheritPackage =, ...)

getRefClass(Class, where =)

参数

Class

类的字符串名称。

在对 getRefClass() 的调用中,此参数也可以是相关类中的任何对象。

fields

字段名称的字符向量或字段的命名列表。将使用引用语义访问结果字段(请参阅“Reference Objects” 部分)。如果参数是一个列表,则列表中的每个元素通常应该是类的字符串名称,在这种情况下,字段中的对象必须来自该类或子类。另一种选择(但通常不推荐)是提供访问器函数;有关访问器函数和相关内部机制,请参阅“Implementation” 部分。

请注意,字段与槽不同。参考类不应定义class-specific 槽。请参阅 “Implementation” 部分中有关插槽的注释。

contains

此类的可选超类向量。如果超类也是引用类,则将继承字段和基于类的方法。

methods

可以在此类的对象上调用的函数定义的命名列表。这些也可以通过在返回的生成器对象上调用 $methods 方法来创建。有关详细信息,请参阅“Writing Reference Methods” 部分。

where

对于 setRefClass ,存储类定义的环境。在包源代码的调用中应省略。

对于 getRefClass ,从中搜索定义的环境。如果包未加载或者您需要具体说明,请使用 asNamespace 和包名称。

inheritPackage

新类中的对象是否应该继承所包含的超类的包环境?默认FALSE。请参阅“Inter-Package超类和外部方法”部分。

...

其他参数传递给 setClass

setRefClass() 返回一个适合从类中隐式创建对象的生成器函数。对此函数的调用需要任意数量的参数,这些参数将传递给初始化方法。如果没有为类或其超类之一定义 initialize 方法,则默认方法需要具有字段之一名称的命名参数和来自此类超类之一的对象的未命名参数(如果有)(但只有本身是引用类的超类才有效)。

生成器函数类似于 setClass 返回的 S4 生成器函数。然而,除了作为生成器函数之外,它还是一个引用类生成器对象,具有各种实用程序的引用类方法。请参阅下面有关引用类生成器对象的部分。

getRefClass() 还返回该类的生成器函数。请注意,无论 where 参数如何,值中的包槽都是类定义中的正确包,该参数仅用于在必要时查找类。

参考对象

普通物体在R在函数调用中作为参数传递,与函数式编程语义一致;也就是说,对作为参数传递的对象所做的更改是函数调用的本地更改。提供参数的对象未更改。

函数模型(有时称为pass-by-value,尽管这对于R)适用于许多统计计算并且是隐式的,例如,在基本计算中R用于拟合统计模型的软件。在其他一些情况下,人们希望处理对象的所有代码都看到完全相同的内容,以便任何计算中所做的更改都会反映在各处。如果对象具有一些“objective”现实,例如用户接口中的窗口,这通常是合适的。

此外,常用语言(包括 Java、C++ 和许多其他语言)都支持假定引用语义的类和方法的版本。相应的编程机制是调用对象上的方法。在里面R我们使用的语法"$"对于此操作;一个调用一个方法,m1比如说,在一个物体上x通过表达式x$m1(...).

此范例中的方法与对象相关联,或者更准确地说与对象的类相关联,这与基于函数的类/方法系统中的方法相反,后者从根本上与函数相关联(在R,例如,一个泛型函数Rsession 有一个包含所有当前已知方法的表)。在本文档中,“类的方法”与“函数的方法”将进行区分。

此范例中的对象通常具有方法操作的命名字段。在里面R实现中,字段是在创建类时定义的。字段本身可以选择具有指定的类,这意味着只有该类或其子类之一的对象才能分配给该字段。默认情况下,字段有类"ANY".

通过引用访问字段。特别地,调用方法可以修改字段的内容。

对此类的编程涉及为特定类编写新方法。在里面R实现,这些方法是R具有零个或多个形式参数的函数。对于标准引用方法,对象本身不是该方法的显式参数。相反,可以通过方法定义中的名称来引用类的字段和方法。实现使用R环境,使字段和其他方法可以通过方法中的名称使用。具体来说,方法的父环境是对象本身。请参阅“Writing Reference Methods” 部分。这种特殊的使用环境是可选的。如果一个方法是用初始形式参数定义的.self,它将作为整个对象传入,并且该方法遵循包中任何函数的标准规则。请参阅“External Methods” 部分

这里说明的软件的目标是提供统一的编程风格R对于处理引用类的软件,是否直接在R或者通过一种 OOP 语言的接口。

编写参考方法

引用方法是作为命名列表元素提供的函数,可以在生成器对象 g 上调用 $methods() 时提供,也可以作为调用 setRefClass 中的参数 methods 提供。这两种机制具有相同的效果,但第一种机制使代码更具可读性。

方法写得和普通的一样R函数,但在其通常形式下有一些特殊函数和限制。与其他一些语言(例如 Python)相比,对象本身不需要作为方法定义中的参数。函数体可以包含对任何其他引用方法的调用,包括从其他引用类继承的方法,并且可以按名称引用对象中的方法和字段。

或者,方法可以是外部方法。这是通过 .self 作为该方法的第一个正式参数来表示的。该方法的主体就像任何普通函数一样工作。这些方法的调用方式与其他方法类似(没有 .self 参数,该参数在内部提供,并且始终引用对象本身)。在方法内部,字段和其他方法以 .self$x 的形式访问。外部方法的存在使得引用类可以继承其他包中超类的包环境;请参阅“External Methods” 部分。

可以使用非局部赋值运算符在方法中修改字段,<<-,如在$edit$undo下面例子中的方法。请注意,需要非本地分配:本地分配<-运算符只是在函数调用中创建一个本地对象,就像在任何函数中一样R函数。安装方法时,将对字段名称的本地分配进行启发式检查,并在检测到任何情况时发出警告。

参考方法应保持简单;如果他们需要做一些专门的事情R计算,该计算应该使用单独的R从引用方法调用的函数。具体来说,方法不能使用封闭环境机制的特殊函数,因为方法的环境用于访问字段和其他方法。特别是,方法不应使用包的命名空间中的非导出条目,因为这些方法可能会被另一个包中的引用类继承。

两个方法名称被特殊解释, initializefinalize 。如果定义了 initialize 方法,则当从类生成对象时将调用该方法。请参阅“Initialization Methods” 部分中对方法$new(...) 的讨论。

如果一个finalize方法被定义,函数将是base reg.finalizer在对象中的环境被垃圾Collector丢弃之前调用它;终结器已注册atexit=TRUE,所以也在末尾运行R会议。有关初始化和终结方法,请参阅矩阵查看器示例。

引用方法本身不能是泛型函数;如果您需要其他基于函数的方法分派,请编写一个单独的通用函数并从该方法中调用它。

有两个特殊的对象名称可用。整个对象可以在方法中通过保留名称 .self 引用。对象.refClassDef包含该对象的类的定义。这些可以作为字段访问,但都是只读的,但有一个例外。原则上,.self 字段可以在$initialize 方法中修改,因为此阶段对象仍在创建中。不建议这样做,因为它会使对象相对于其类无效。

可用的方法包括从超类继承的方法,如“Inheritance”部分中讨论的。

只有实际使用的方法才会包含在与单个对象相对应的环境中。要声明某个方法需要特定的其他方法,第一个方法应包含对 $usingMethods() 的调用,并以其他方法的名称作为参数。如果间接使用其他方法(例如,通过 sapply()do.call() ),则以这种方式声明方法至关重要。如果直接调用的话,代码分析就能找到。然而,声明该方法在任何情况下都是无害的,并且可能有助于源代码的可读性。

该方法的文档可以通过以下方式获得:$help生成器对象的方法。类的方法没有记录在Rd格式用于R函数。相反,$helpmethod 以Python 的风格打印方法的调用顺序,后跟方法定义中的self-documentation。如果方法主体的第一个元素是文字字符串(可能是多行),则该字符串将被解释为文档。请参阅示例中的方法定义。

初始化方法

如果该类具有为 $initialize() 定义的方法,则在创建引用对象后将调用该方法。您应该为需要进行一些特殊初始化的类编写这样的方法。特别是,建议使用引用方法,而不是 S4 通用函数 initialize() 的方法,因为在初始化字段之前,引用对象需要进行一些特殊的初始化。与 S4 类一样,方法是为 $initialize() 而不是为 $new() 编写的,这既是出于前面的原因,也是因为 $new() 在生成器对象上调用,并且将是该类的方法。

$initialize() 的默认方法相当于调用方法 $initFields(...) 。命名参数将初始值分配给相应的字段。未命名参数必须是此类的对象或此类的引用超类。字段将被初始化为此类对象中字段的内容,但命名参数会覆盖相应的继承字段。请注意,字段只是分配的。如果字段本身是引用对象,则不会复制该对象。新的和以前的对象将共享引用。此外,从未命名参数分配的字段也算作锁定字段的分配。要覆盖锁定字段的继承值,新值必须是初始化调用中的命名参数之一。稍后分配该字段将导致错误。

初始化方法在设计时需要小心。引用类的生成器将在不带参数的情况下调用,例如在复制对象时。为了确保这些调用不会失败,该方法必须具有所有参数的默认值或检查 missing() 。该方法应包含 ... 作为参数,并通过 $callSuper() 传递(或 $initFields(),如果您知道您的超类没有初始化方法)。这允许将来的类定义使用附加字段来子类化此类。

继承

参考类通过使用标准从其他参考类继承R遗产;也就是说,通过将超类包含在contains=创建新类时的参数。参考超类的名称位于槽中refSuperClasses类的定义。引用类也可以继承普通的 S4 类,但如果混合引用字段和非引用槽,这通常是一个坏主意。请参阅“Implementation” 部分中的评论。

类字段是继承的。仅当重写类是继承字段的类的子类时,类定义才可以重写超类中的同名字段。这确保了字段中的有效对象对于超类也保持有效。

继承的方法的安装方式与直接指定的方法相同。方法中的代码可以像直接指定方法一样引用继承的方法。

方法可以覆盖超类中的同名方法。重写方法可以通过 callSuper(...) 调用超类方法,如下所述。

为所有对象提供的方法

所有参考类都继承自类 "envRefClass" 。所有引用对象都可以使用以下方法。

$callSuper(...)

调用从引用超类继承的方法。该调用仅在另一个方法中才有意义,并且将解析为调用同名的继承方法。 $callSuper 的参数被传递给超类版本。请参阅示例中的矩阵查看器类。

请注意,必须显式提供超类方法的预期参数;与函数方法的类似机制相比,没有自动提供参数的约定。

$copy(shallow = FALSE)

创建对象的副本。有参考类,与普通类不同R对象,仅仅为对象分配不同的名称并不会创建独立的副本。如果shallowFALSE,任何本身就是引用对象的字段也将被复制,并且对其字段进行类似的递归复制。否则,虽然将字段重新分配给新的引用对象不会产生副作用,但修改此类字段仍将反映在该对象的两个副本中。该参数对字段中的非引用对象没有影响。当某些字段存在引用对象但断言它们不会被修改时,使用shallow = TRUE将节省一些内存和时间。

$field(name, value)

通过一个参数,返回字符串 name 的对象字段。对于两个参数,相应的字段被分配为 value 。赋值检查 name 是否指定了有效字段,但 single-argument 版本将尝试从对象环境中获取该名称的任何内容。

当必须计算字段名称或循环多个字段时,$field() 方法取代了直接使用字段名称。

$export(Class)

返回将对象强制为Class(通常是对象类的超类之一)的结果。调用该方法对对象本身没有副作用。

$getRefClass(); $getClass()

它们分别有效地返回生成器对象和该对象的引用类的正式类定义。

$import(value, Class = class(value))

将对象value导入到当前对象中,替换当前对象中的相应字段。对象 value 必须来自当前对象类的超类之一。如果提供了参数 Class,则首先将 value 强制为该类。

$initFields(...)

从提供的参数初始化对象的字段。通常仅从具有 $initialize() 方法的类调用此方法。它对应于引用类的默认初始化。如果存在槽和非引用超类,这些也可以在 ... 参数中提供。

通常,专门的 $initialize() 方法会执行自己的计算,然后调用 $initFields() 来执行标准初始化,如下面示例中的 matrixViewer 类所示。

$show()

自动打印对象时会调用此方法,类似于show 函数。为类 "envRefClass" 定义了通用方法。用户定义的引用类通常会定义自己的方法:请参见下面的示例。

请注意示例中的两点。与任何 show() 方法一样,最好显式打印该类以允许子类使用该方法。其次,要从方法中调用函数 show(),而不是调用 $show() 方法本身,请显式引用 methods::show()

$trace(what, ...) , $untrace(what)

trace 函数的跟踪和调试函数应用于参考方法 what

可以提供 trace 函数的所有参数,但 signature 除外,该参数没有意义。

可以在对象或类的生成器上调用引用方法。有关详细信息,请参阅下面的“调试”部分。

$usingMethods(...)

此方法使用的引用方法被命名为带引号或不带引号的参数。在安装本方法的代码分析阶段,将包含声明的方法。必须声明以非标准方式使用的任何方法(例如,通过 apply 函数)。直接调用的方法不需要声明,但这样做是无害的。 $usingMethods() 在运行时不执行任何操作。

对象还继承两个保留字段:

.self

对整个对象的引用;

.refClassDef

类定义。

定义的字段不应覆盖这些字段,通常定义名称以 "." 开头的字段是不明智的,因为实现可能会将此类名称用于特殊目的。

外部方法; Inter-Package 超类

引用类中方法的环境是对象本身,作为一个环境。这允许该方法直接引用字段和其他方法,而无需使用整个对象和 "$" 运算符。该环境的父环境是定义引用类的包的命名空间。该方法中的计算可以访问包命名空间中的所有对象(无论是否导出)。

当定义一个包含另一个包中的引用超类的类时,哪个包命名空间应该具有该角色是不明确的。参数 inheritPackagesetRefClass() 控制新对象的环境是应该从另一个包中的继承类继承,还是继续从当前包的命名空间继承。

如果超类是“lean”,方法很少,或者主要是为了支持一系列子类而存在,那么最好继续使用新包的环境。另一方面,如果超类最初是作为独立编写的,则此选择可能会使现有的超类方法无效。为了使超类方法继续工作,它们必须仅使用其包中的导出函数,并且新包必须导入这些函数。

无论哪种方式,可能需要编写一些方法,这些方法不采用引用类方法的标准模型,但其行为本质上与处理引用类对象的普通函数相同。

该机制是识别外部方法。外部方法被编写为函数,其中第一个参数名为 .self 代表引用类对象。该函数作为引用类方法的定义提供。该方法将被自动调用,第一个参数是当前对象,其他参数(如果有)从实际调用传递。

由于外部方法是其包的源代码中的普通函数,因此它可以访问命名空间中的所有对象。引用类中的字段和方法必须以 .self$name 的形式引用。

如果由于某种原因您不想使用 .self 作为第一个参数,则可以将函数 f() 显式转换为 externalRefMethod(f) ,该函数返回类 "externalRefMethod" 的对象,该对象可以作为类。第一个参数仍然对应于整个对象。

可以为任何引用类提供外部方法,但除非需要,否则没有明显的优势。它们需要编写更多的工作,阅读起来更困难,并且执行起来(稍微)慢一些。

注意:如果您是一个包的作者,其引用类可能在其他包中被子类化,则可以通过编写仅使用包中导出函数的方法来完全避免这些问题,以便所有方法都可以在另一个包中工作导入你的。

参考类生成器

setRefClass 的调用定义指定的类并返回该类的 “generator function” 对象。该对象具有类 "refObjectGenerator" ;它通过"classGeneratorFunction"继承自"function",并且可以被调用以从引用类生成新对象。

返回的对象也是一个引用类对象,尽管不是标准结构。它可用于以通常的方式调用引用方法和访问字段,但它不是直接作为环境实现,而是有一个作为槽的辅助生成器对象,一个标准引用对象(类 "refGeneratorSlot" )。请注意,如果想使用子类扩展参考类生成器函数,则应通过子类化 "refGeneratorSlot" 而不是 "refObjectGenerator" 来完成。

这些字段是def(类定义)和className(类的字符串名称)。方法从类生成对象,访问引用方法的帮助,并为类定义新的引用方法。目前可用的方法有:

$new(...)

该方法相当于调用 setRefClass 返回的生成器函数。

$help(topic)

打印有关该主题的简短帮助。识别的主题是参考方法名称,无论是否引用。

打印的信息是该方法的调用序列,加上self-documentation(如果有)。引用方法可以将初始字符串或向量作为定义该方法的函数体中的第一个元素。如果是这样,该字符串将被视为该方法的self-documentation(有关详细信息,请参阅“Writing Reference Methods”部分)。

如果未给出主题或主题不是方法名称,则打印类的定义。

$methods(...)

如果不带参数,则返回此类的引用方法的名称。使用一个字符串参数,返回该名称的方法。

命名参数是方法定义,它们将安装在类中,就像它们在 methods 参数中提供给 setRefClass() 一样。为了使源代码更清晰,建议以这种方式提供方法,而不是调用 setRefClass() 。有关详细信息,请参阅“Writing Reference Methods” 部分。

类的所有方法都应该在定义该类的源代码中定义,通常作为包的一部分。特别是,不能在具有命名空间的附加包中的类中重新定义方法:类方法检查类定义的锁定绑定。

新方法可以按名称引用任何当前定义的方法(包括在 $methods() 调用中提供的其他方法)。请注意,先前定义的方法不会被重新分析,这意味着它们不会调用新方法(除非它重新定义同名的现有方法)。

要删除方法,请提供NULL 作为其新定义。

$fields()

返回字段列表,每个字段都有其对应的类。在定义中为其提供访问器函数的字段具有类 "activeBindingFunction"

$lock(...)

参数中指定的字段被锁定;具体地,调用lock方法后,该字段可以被设置一次。任何进一步尝试设置它都会生成错误。

如果不带参数调用,该方法将返回锁定字段的名称。

由显式访问器函数定义的字段不能被锁定(另一方面,如果使用参数调用访问器函数,则可以定义为生成错误)。

所有锁定字段的代码通常应该是类定义的一部分;也就是说,字段的只读性质是类定义的一部分,而不是稍后添加的动态属性。特别是,字段不能锁定在具有命名空间的附加包中的类中:类方法检查类定义的锁定绑定。锁定的字段随后无法解锁。

$trace(what, ..., classMethod = FALSE)

为此类生成的对象建立方法 what 的跟踪版本。生成器对象跟踪的工作方式类似于类中对象的 $trace() 方法,但有两个区别。由于它更改了类对象本身中的方法定义,因此跟踪适用于所有对象,而不仅仅是调用跟踪方法的对象。

其次,可选参数 classMethod = TRUE 允许跟踪生成器对象本身的方法。默认情况下,what 被解释为该对象作为生成器的类中的方法名称。

$accessors(...)

许多使用 OOP 编程范式的系统推荐或强制执行与每个字段对应的 getter 和 setter 方法,而不是通过名称直接访问。如果您喜欢这种风格,并且想要通过 x$getAbc() 提取名为 abc 的字段并通过 x$setAbc(value) 对其进行分配,则 $accessors 方法是一个方便的函数,可以为指定字段创建此类 getter 和 setter 方法。否则没有理由使用这个机制。特别是,它与 “Reference Objects” 部分中说明的通过函数定义字段的一般能力无关。

执行;参考类作为 S4 类

引用类被实现为 S4 类,其数据部分类型为 "environment" 。字段对应于环境中的命名对象。与函数关联的字段被实现为 active binding 。特别是,具有指定类的字段被实现为主动绑定的特殊形式,以强制对该字段进行有效分配。

作为一项相关函数,提供给 setRefClassfields= 列表中的元素可以是访问器函数,这是一种只有一个参数的函数,如果不带参数调用,则返回该字段,否则将其设置为参数的值。访问器函数在内部使用并用于 inter-system 接口应用程序,但通常不推荐,因为它们模糊了字段作为对象内数据的概念。

一个领域,比如说data,通常可以通过以下形式的表达式来访问x$data对于相关类中的任何对象。在此类的内部方法中,可以通过名称访问该字段data。未锁定的字段可以通过以下形式的表达式设置x$data <- value。在内部方法内部,可以通过以下形式的表达式分配字段x <<- value。请注意base assignOps操作符。标准R该运算符的解释用于将其分配到对象的环境中。如果该字段定义了访问器函数,则获取和设置将调用该函数。

当在对象上调用方法时,定义该方法的函数将安装在该对象的环境中,并且环境与该函数的环境相同。

参考类可以具有与任何 S4 类相同意义上的有效性方法(请参阅setValidity)。这些方法通常是个好主意;他们将被调用validObject并且如果定义了一个有效性方法,则在创建引用对象时将调用该方法(从 3.4 版开始)R在)。请记住这些是 S4 方法。该函数将被调用object作为其论点。必须使用以下方式访问字段和方法$.

注:插槽。由于实现的缘故,新的引用类可以从非引用 S4 类以及引用类继承,并且可以在定义中包含class-specific 槽。如果非引用类中的槽被认为是字段的替代品,那么这通常是一个坏主意。插槽将一如既往地按函数进行处理。因此,对槽和字段的更改将表现得不一致,混合同一对象属性的函数和引用范例,概念上不清楚并且容易出错。此外,该类的初始化方法必须从槽中整理字段,这很有可能为该类的子类创建异常行为。

然而,从 class union 继承是一个合理的策略(联合体的所有成员都可能是引用类)。

调试

标准R调试和跟踪工具可应用于参考方法。参考方法可以传递给debug及其来自对象的亲属,以调试该对象上的进一步方法调用;例如,debug(xx$edit).

trace 函数的参考方法版本的使用更为灵活。相应的$trace() 引用方法可用于对象或引用类生成器(下例中的xx$trace()mEdit$trace())。在对象上使用 $trace() 会设置跟踪版本,以便将来调用该对象的指定方法。在类的生成器上使用 $trace() 会为该类中的所有未来对象(如果未声明或先前调用该方法,有时还会为该类中的现有对象)设置跟踪版本。

在任何一种情况下,标准 trace 函数的所有参数都可用,但 signature= 除外,它没有意义,因为引用方法不能是 S4 泛型函数。这包括用于交互式调试的典型样式 trace(what, browser) 和用于交互式编辑参考方法的 trace(what, edit = TRUE)

例子

## a simple editor for matrix objects.  Method  $edit() changes some
## range of values; method $undo() undoes the last edit.
mEdit <- setRefClass("mEdit",
      fields = list( data = "matrix",
        edits = "list"))

## The basic edit, undo methods
mEdit$methods(
     edit = function(i, j, value) {
       ## the following string documents the edit method
       'Replaces the range [i, j] of the
        object by value.
        '
         backup <-
             list(i, j, data[i,j])
         data[i,j] <<- value
         edits <<- c(edits, list(backup))
         invisible(value)
     },
     undo = function() {
       'Undoes the last edit() operation
        and update the edits field accordingly.
        '
         prev <- edits
         if(length(prev)) prev <- prev[[length(prev)]]
         else stop("No more edits to undo")
         edit(prev[[1]], prev[[2]], prev[[3]])
         ## trim the edits list
         length(edits) <<- length(edits) - 2
         invisible(prev)
     })

## A method to automatically print objects
mEdit$methods(
     show = function() {
       'Method for automatically printing matrix editors'
       cat("Reference matrix editor object of class",
          classLabel(class(.self)), "\n")
       cat("Data: \n")
       methods::show(data)
       cat("Undo list is of length", length(edits), "\n")
     }
     )

xMat <- matrix(1:12,4,3)
xx <- mEdit(data = xMat)
xx$edit(2, 2, 0)
xx
xx$undo()
mEdit$help("undo")
stopifnot(all.equal(xx$data, xMat))

utils::str(xx) # show fields and names of methods

## A method to save the object
mEdit$methods(
     save = function(file) {
       'Save the current object on the file
        in R external object format.
       '
         base::save(.self, file = file)
     }
)

tf <- tempfile()
xx$save(tf)


## Not run: 
## Inheriting a reference class:  a matrix viewer
mv <- setRefClass("matrixViewer",
    fields = c("viewerDevice", "viewerFile"),
    contains = "mEdit",
    methods = list( view = function() {
        dd <- dev.cur(); dev.set(viewerDevice)
        devAskNewPage(FALSE)
        matplot(data, main = paste("After",length(edits),"edits"))
        dev.set(dd)},
        edit = # invoke previous method, then replot
          function(i, j, value) {
            callSuper(i, j, value)
            view()
          }))

## initialize and finalize methods
mv$methods( initialize =
  function(file = "./matrixView.pdf", ...) {
    viewerFile <<- file
    pdf(viewerFile)
    viewerDevice <<- dev.cur()
    dev.set(dev.prev())
    callSuper(...)
  },
  finalize = function() {
    dev.off(viewerDevice)
  })

## debugging an object: call browser() in method $edit()
xx$trace(edit, browser)

## debugging all objects from class mEdit in method $undo()
mEdit$trace(undo, browser)

## End(Not run)
 

参考

Chambers, John M. (2016) Extending R, Chapman & Hall. (Chapters 9 and 11.)

相关用法


注:本文由纯净天空筛选整理自R-devel大神的英文原创作品 Objects With Fields Treated by Reference (OOP-style)。非经特殊声明,原始代码版权归原作者所有,本译文未经允许或授权,请勿转载或复制。