当也在另一个包中设置时,S4 方法失败

S4 method fails when also set in another package

当我尝试设置一个也在另一个包中定义的方法时,我遇到了一个奇怪的问题。可以找到演示此问题的示例包 here

关键是我尝试设置的typeof方法。我对 typeof 使用 setMethod 函数,当我构建包并在普通 S4 class.

上试用它时它可以工作
x <- new("A", x = 2)
typeof(x)
[1] "typeof was called"
[1] "myClassA"

但是,如果我在加载 s4test 包之前加载另一个先前也设置了 typeof 的包(例如 bigmemory),如果直接调用它,它会继续正常工作。

library(bigmemory)
library(s4test)
x <- new("A", x = 2)
typeof(x)
[1] "typeof was called"
[1] "myClassA"

但是,如果 typeof 方法被另一个方法内部调用(例如 nrow 参见 here),那么它将失败并且只有 returns S4

nrow(x)
[1] "A"
attr(,"package")
[1] "s4test"
Function: typeof (package base)
x="ANY"
x="big.matrix"
x="myClass"

A connection with                      
description "stdout"  
class       "terminal"
mode        "w"       
text        "text"    
opened      "opened"  
can read    "no"      
can write   "yes"     
[1] "S4"

问题是 Depends 并不总是确定的。 Imports 比使用 Depends 更安全,您可以阅读更多有关此内容的信息 here

在全局环境中,您的包裹正在​​使用 bigmemory::typeof,并且在 nrow base::typeof.

> base::typeof(x)
[1] "S4"
> bigmemory::typeof(x)
[1] "typeof was called"
[1] "myClassA"

您可以通过直接引用您要使用的包名称的函数来解决这个问题。

胡安·安东尼奥走在正确的轨道上, 但它与 DependsImports.

无关

答案在 Methods_for_Nongenerics 的文档条目中 (适用于 typeof,因为它不是 base 中的通用函数):

In writing methods for an R package, it's common for these methods to apply to a function (in another package) that is not generic in that package; that is, there are no formal methods for the function in its own package, although it may have S3 methods. The programming in this case involves one extra step, to call setGeneric() to declare that the function is generic in your package.

Calls to the function in your package will then use all methods defined there or in any other loaded package that creates the same generic function. Similarly, calls to the function in those packages will use your methods.

The original version, however, remains non-generic. Calls in that package or in other packages that use that version will not dispatch your methods except for special circumstances...

您可以在干净的 R 会话中通过 运行 以下内容查看此效果:

environment(typeof)
<environment: namespace:base>

library(s4test)
environment(typeof)
<environment: 0x0000000017ca5e58>

加载包后, typeof 函数已被通用化, 但不在其原始环境中。 也可以看到新泛型的环境的enclosing环境:

parent.env(environment(typeof))
<environment: namespace:base>

调用函数时, R首先查看当前环境, 并在找不到东西时查看封闭环境。 nrow 函数是 base 包的一部分 (而且它也不是通用的, 虽然这与这里无关), 这意味着它将在看到通用的之前找到自己的非通用 typeof

Methods_for_Nongenerics 的文档解释了不同的场景, 但对于你的情况, 您实际上可以执行以下操作:

setMethod('nrow', signature(x="myClass"),
          function(x) {
              # find the generic version from the global environment and bind it here
              typeof <- get("typeof", .GlobalEnv)
              switch(typeof(x),
                     "myClassA" = "A rows",
                     "myClassB" = "B rows")
          }
)

nrow(x)
[1] "typeof was called"
[1] "A rows"