【问题标题】:R reference classes - how to determine if you're in an inherited method?R 参考类 - 如何确定你是否在继承的方法中?
【发布时间】:2014-01-22 07:50:09
【问题描述】:

对于给定的引用类方法,如何确定它是否被继承?更一般地说,如何确定我在继承树上有多远?

例如,如果我的设置是:

A <- setRefClass("A",
        methods = list(foo = function() whosMethod())
    )
B <- setRefClass("B",
        contains = "A",
        methods = list(bar = function() whosMethod())
    )
b <- B()

理想情况下,我希望 whosMethod() 给我类似的东西

> b$foo()
[1] "A"         # or maybe a numeric value like: [1] 1L

> b$bar()
[1] "B"         # or maybe a numeric value like: [1] 0L

请注意,这与class(.self) 明显不同,后者在上面的示例中总是返回"B"

动机 - 自定义事件

我希望除了方法之外的其他事物具有类似继承的行为,例如自定义事件。我的方法可能是raise(someEvent),并且在实例化期间我传递事件处理程序来处理这些事件,例如

MyDatabase <- setRefClass(....)
datasourceA <- MyDatabase(....,
    eventHandlers = list(
        someEvent = function() message("Hello from myObj!"),
        beforeInsert = function(data) {
            if (!dataIsValid(data))
                stop("Data is not valid!")
        }
    )
)

现在,如果一个子类定义了一个已经由父类定义的事件处理程序,那么我需要知道应该覆盖哪个事件处理程序。特别是,如果一个methodA()someEvent 注册handlerA() 和一个子类 中的methodB()同一事件 注册handlerB(),当尝试在methodA() 中注册handlerA() 我需要知道我在父方法中,这样如果handlerB() 已经注册,我不会覆盖它。

如果能够从子事件处理程序调用父事件处理程序也很好,例如方法可用的callSuper()

【问题讨论】:

  • 为什么要这样做?
  • @hadley - 在上面添加了我的动机。谢谢。
  • 我做了一点探索,我会说这是不可能的。即使有可能,我也不认为有一个非平凡的定义可以很好地用于多重继承:你有一个继承图,而不是继承树。您是否有理由不想使用 R 中现有的条件处理机制?即adv-r.had.co.nz/Exceptions-Debugging.html#custom-signal-classes
  • @hadley,使用现有的条件处理机制意味着每次我使用我的对象时,我都必须用withCallingHandlers(...., condA = ...., condB = ....) 包装它,列出每个条件处理程序,这很烦人,我可能会忘记去做。我只想能够在实例化时定义一些集合处理程序,并且知道我的事件将始终在raise() 处触发。

标签: r oop inheritance reference-class


【解决方案1】:

试试这个:

遍历继承图,得到各自的方法

methodsPerClass <- function(x) {
    if (!inherits(x, "envRefClass")) {
        stop("This only works for Reference Class objects")
    }
    ## Get all superclasses of class of 'x' //
    supercl <- selectSuperClasses(getClass(class(b)), directOnly=FALSE)
    ## Get all methods per superclass //
    out <- lapply(c(class(x), supercl), function(ii) {
        ## Get generator object //
        generator <- NULL
        if (inherits(getClass(ii), "refClassRepresentation")) {
            generator <- getRefClass(ii)
        }
        ## Look up method names in class defs //
        out <- NULL
        if (!is.null(generator)) {
            out <- names(Filter(function(x) {
                    attr(x, "refClassName") == generator$className
                }, 
                as.list(generator$def@refMethods))
            )        
        }
        return(out)
    })
    names(out) <- supercl
    ## Filter out the non-reference-classes //
    idx <- which(sapply(out, is.null))
    if (length(idx)) {
        out <- out[-idx]
    }
    ## Nicer name for actual class of 'x' //
    idx <- which(names(out) == "envRefClass") 
    if (length(idx)) {
        names(out)[idx] <- class(x)
    }
    return(out)
}

我确信可以想出一些更好的方法来过滤掉非引用类,以便在最后摆脱“idx”部分,但它确实有效。

这会给你:

methodsPerClass(x=b)
$A
[1] "bar"

$B
[1] "foo"

$.environment
 [1] "import"       "usingMethods" "show"         "getClass"     "untrace"     
 [6] "export"       "callSuper"    "copy"         "initFields"   "getRefClass" 
[11] "trace"        "field"     

查询哪些方法具体属于哪个类

whosMethod <- function(x, method) {
    mthds <- methodsPerClass(x=x)
    out <- lapply(method, function(m) {
        pattern <- paste0("^", m, "$")
        idx <- which(sapply(mthds, function(ii) {
            any(grepl(pattern, ii))
        }))
        if (!length(idx)) {
            stop(paste0("Invalid method '", m, 
                "' (not a method of class '", class(x), "')"))
        }
        out <- names(idx)
    })
    names(out) <- method
    return(out)
}

这会给你:

whosMethod(x=b, method="foo")
$foo
[1] "B"

whosMethod(x=b, method=c("foo", "bar"))
$foo
[1] "B"

$bar
[1] "A"

whosMethod(x=b, method=c("foo", "bar", "nonexisting"))
Error in FUN(c("foo", "bar", "nonexisting")[[3L]], ...) : 
  Invalid method 'nonexisting' (not a method of class 'B')

为“B”类的所有方法运行它:

whosMethod(x=b, method=unlist(methodsPerClass(x=b)))
$bar
[1] "A"

$foo
[1] "B"

$import
[1] ".environment"

$usingMethods
[1] ".environment"

$show
[1] ".environment"

$getClass
[1] ".environment"

$untrace
[1] ".environment"

$export
[1] ".environment"

$callSuper
[1] ".environment"

$copy
[1] ".environment"

$initFields
[1] ".environment"

$getRefClass
[1] ".environment"

$trace
[1] ".environment"

$field
[1] ".environment"

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-11-13
    • 2015-05-04
    • 2011-08-27
    • 2014-03-27
    • 1970-01-01
    • 2010-10-28
    • 2015-07-16
    • 2010-09-12
    相关资源
    最近更新 更多