如果所有类都需要存在于包的命名空间中(而不是GlobalEnv
中),那么如何将一堆相互继承的R6类转换为S4类,同时保留继承结构?
在.GlobalEnv
中定义R6类(如使用source()
进行采购时)和使用setOldClass()
调用where = .GlobalEnv
时,一切正常。
但是当在包的命名空间中定义R6类时(如调用devtools::load_all()
时),我无法使它工作:
在.GlobalEnv
中定义R6类:
Object <- R6Class("Object", portable = TRUE, public = list(
foo = function() "foo")
)
Api <- R6Class("Api", inherit = Object, portable = TRUE,
public = list(bar = function() "bar")
)
Module <- R6Class("Module", inherit = Api, portable = TRUE,
public = list(fooBar = function() "fooBar")
使用setOldClass()
(where = .GlobalEnv
的默认设置)呼叫where
:
setOldClass(c("Object", "R6"))
setOldClass(c("Api", "Object"))
setOldClass(c("Module", "Api"))
当R6类在包的名称空间内定义时(如何&#34;采购&#34;使用devtools::load_all()
而不是source()
),我认为我需要帐户为此提供明确的where
:
where <- if ("package:r6.s4" %in% search()) {
as.environment("package:r6.s4")
} else {
.GlobalEnv
}
try(setOldClass(c("Object", "R6"), where = where))
try(setOldClass(c("Api", "Object"), where = where))
try(setOldClass(c("Module", "Api"), where = where))
然而,这让我有以下错误:
setOldClass中出错(c(&#34;模块&#34;,&#34; Api&#34;),其中= where): “模块”的旧式类信息不一致;该类已定义但不扩展“Api”,并且无效作为数据部分
我尝试尽可能轻松地重现此问题,因此您可以在GitHub repository
找到r6.s4
包
请再次注意,您必须运行devtools::load_all()
(或点击RStudio中的CRTL + SHFT + L
)才能重现错误。
此外,这unit test可能有助于弄清楚发生了什么。
答案 0 :(得分:1)
我想我明白了。
setOldClass(c("Module", "Api"))
失败的原因是由于包Rcpp
具有定义了相同名称的类。
require("R6")
> getClass("Module")
Class "Module" [package "Rcpp"]
Slots:
Name: .xData
Class: environment
Extends:
Class ".environment", directly
Class "environment", by class ".environment", distance 2, with explicit coerce
Class "refObject", by class ".environment", distance 3, with explicit coerce
调用setOldClass()
的最佳位置似乎在.onAttach()
内,因为在此阶段包已完全加载,因此存在where
参数可以存在的命名空间环境指向。
.onAttach <- function(libname, pkgname) {
where <- as.environment("package:r6.s4")
clss <- list(
c("Object", "R6"),
c("Api", "Object"),
c("Module2", "Api")
)
sapply(clss, function(cls) {
try(setOldClass(cls, where = where))
})
}
在.onAttach()
内,您需要保持良好状态,而不是“重载”setOldClass()
在先前的包加载上设置的类。这就是为什么类似的东西可能有意义的原因:
.onAttach <- function(libname, pkgname) {
where <- as.environment("package:r6.s4")
clss <- list(
c("Object", "R6"),
c("Api", "Object"),
c("Module2", "Api")
)
sapply(clss, function(cls) {
idx <- sapply(cls, isClass)
try(sapply(cls[idx], removeClass, where = where))
try(setOldClass(cls, where = where))
})
}
我喜欢R6
依靠实际的生成器对象而不仅仅是类名很多的方法,因为它使您能够使用{{ 1}}因此保持类与所有其他包组件一样有条理。但遗憾的是,当通过::
为他们注册S4等价物时,这种范例似乎已经丢失了。这种情况让我回到了我对name clashes for classes - setOldClass()
日益增加的风险的抱怨。
对于那些对我的试错过程的细节感兴趣的人:我试图将包变成一种自我引用。检查文件R/classes.r,tests/testthat/test-S4.r以及有关如何检查和处理名称冲突R/name_clashes.r的原型代码。