【问题标题】:Remove/undef a class method删除/取消定义类方法
【发布时间】:2011-01-05 23:02:12
【问题描述】:

您可以像这样为类动态定义类方法:

class Foo
end

bar = %q{def bar() "bar!" end}
Foo.instance_eval(bar)

但是你如何做相反的事情:删除/取消定义一个类方法?我怀疑 Module 的 remove_methodundef_method 方法可能可以用于此目的,但我在谷歌搜索数小时后看到的所有示例都是用于删除/取消定义 instance 方法,不是类方法。或者,您也可以将一种语法传递给 instance_eval 来执行此操作。

提前致谢。

【问题讨论】:

    标签: ruby metaprogramming


    【解决方案1】:
    class Foo
      def self.bar
        puts "bar"
      end
    end
    
    Foo.bar    # => bar
    
    class <<Foo
      undef_method :bar
    end
    # or
    class Foo
      singleton_class.undef_method :bar
    end
    
    Foo.bar    # => undefined method `bar' for Foo:Class (NoMethodError)
    

    当你定义一个像 Foo.bar 这样的类方法时,Ruby 把它放在 Foo 的单例类中。 Ruby 不能把它放在 Foo 中,因为那样它将是一个实例方法。 Ruby 创建 Foo 的单例类,将单例类的超类设置为 Foo 的超类,然后将 Foo 的超类设置为单例类:

    Foo -------------> Foo(singleton class) -------------> Object
            super      def bar             super
    

    有几种方法可以访问单例类:

    • class &lt;&lt;Foo,
    • Foo.singleton_class,
    • class Foo; class &lt;&lt; self,常用来定义类方法。

    请注意,我们使用了undef_method,我们可以使用remove_method。前者阻止对该方法的任何调用,后者仅删除当前方法,如果存在则回退到超级方法。请参阅Module#undef_method 了解更多信息。

    【讨论】:

    • 我原以为不使用 Eigenclass 是可能的,至少在 1.9 中是这样。
    • @Andrew,也许是这样。唉,我不知道。
    • 这在 Ruby1.9.3 中对我不起作用。我仍然能够调用删除的方法。
    • @joseph.hainline - 这很有趣!我刚刚确认上述方法在 MRI 1.8.3-p374、MRI 1.9.3-p484、MRI 2.0.0-p247 和 MRI 2.1.0 中有效。您是否正在做一些不同的事情,无论是在删除方法时,还是在调用它时,或者使用非 MRI Ruby 时?
    • @joseph.hainline - 如果您在超类中有该方法,则该方法在您调用 remove_method 后仍然可以调用。你可以使用 undef_method 来阻止它。
    【解决方案2】:

    这也适用于我(不确定 undef 和 remove_method 之间是否存在差异):

    class Foo
    end
    
    Foo.instance_eval do
      def color
        "green"
      end
    end
    
    Foo.color # => "green"
    
    Foo.instance_eval { undef :color }
    
    Foo.color # => NoMethodError: undefined method `color' for Foo:Class
    

    【讨论】:

    • 这对我有用。我在一个对象上调用它,它只在对象级别删除它。 Foo.new.instance_eval { undef :color } 也可以。
    • removed_method 删除接收器类的方法,其中 undef_method 删除了继承类中的所有方法,包括接收器类。
    【解决方案3】:

    您可以通过两种简单的方式删除方法。剧烈的

    Module#undef_method( ) 
    

    删除所有方法,包括继承的方法。善良的人

    Module#remove_method( ) 
    

    从接收器中删除方法,但它 保留继承的方法。

    见下面2个简单的例子-

    示例 1 使用 undef_method

    class A 
        def x
            puts "x from A class"
        end
    end
    
    class B < A
        def x
            puts "x from B Class"
        end
        undef_method :x
    end
    
    obj = B.new
    obj.x
    

    结果 - main.rb:15:in ': undefined methodx' for # (NoMethodError)

    使用 remove_method

    的示例 2
    class A 
        def x
            puts "x from A class"
        end
    end
    
    class B < A
        def x
            puts "x from B Class"
        end
        remove_method :x
    end
    
    obj = B.new
    obj.x
    

    结果 - $ruby main.rb

    来自A类的x

    【讨论】:

      【解决方案4】:

      我想我无法评论 Adrian 的回答,因为我没有足够的信誉,但他的回答帮助了我。

      我发现:undef 似乎完全从存在中删除了该方法,而 remove_method 将其从该类中删除,但仍将在超类或已在此类上扩展的其他模块上定义,等等。

      【讨论】:

      • 在 Ruby 2.4 中它现在看起来是 undef_method
      【解决方案5】:

      如果您想删除名称为动态计算的方法,您应该使用如下特征类:

      class Foo
        def self.bar
          puts "bar"
        end
      end
      
      name_of_method_to_remove = :bar
      eigenclass = class << Foo; self; end
      eigenclass.class_eval do
        remove_method name_of_method_to_remove
      end
      

      这种方式比其他答案更好,因为这里我使用了带有块的 class_eval。由于您现在阻止查看当前命名空间,因此您可以使用变量来动态删除方法

      【讨论】:

        【解决方案6】:

        Object.send(:remove_const, :Foo)

        【讨论】:

        • 这不是删除整个班级吗?
        • 从技术上讲,这个答案并非不准确(即,事实上,这是一种删除类方法的方法),因为通过删除类 Foo 它也删除了 Foo 中的所有类方法:P:P: P。我的意思是,这显然不是 OP 真正想要的,但从技术上讲,它不是 false。其他技术上正确的答案:1)杀死包含的Ruby进程; 2) 重启操作系统; 3)把电脑扔进湖里; 4)在附近投下核弹; 5)触发超新星; 6)等待宇宙的热寂。
        猜你喜欢
        • 2011-11-26
        • 1970-01-01
        • 2022-07-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-04-11
        • 1970-01-01
        相关资源
        最近更新 更多