【问题标题】:Ruby: Calling class method from instanceRuby:从实例调用类方法
【发布时间】:2011-02-01 10:32:21
【问题描述】:

在 Ruby 中,如何从某个类的实例调用一个类方法?说我有

class Truck
  def self.default_make
    # Class method.
    "mac"
  end

  def initialize
    # Instance method.
    Truck.default_make  # gets the default via the class's method.
    # But: I wish to avoid mentioning Truck. Seems I'm repeating myself.
  end
end

Truck.default_make 行检索默认值。但是有没有办法不提Truck就这么说?好像应该有。

【问题讨论】:

    标签: ruby class-method


    【解决方案1】:

    而不是引用类的字面名称,在实例方法中你可以直接调用self.class.whatever

    class Foo
        def self.some_class_method
            puts self
        end
    
        def some_instance_method
            self.class.some_class_method
        end
    end
    
    print "Class method: "
    Foo.some_class_method
    
    print "Instance method: "
    Foo.new.some_instance_method
    

    输出:

    类方法:Foo 实例方法:Foo

    【讨论】:

    • 我想在 ruby​​ 中看到一些从实例调用类方法的快捷方式。即 :>some_class_method 而不是 self.class.some_class_method
    • 虽然这是正确的答案,但遗憾的是,“self.class”比类名“Truck”更容易打字,更不容易阅读。哦,好吧....
    • @MattConnolly,它是相对的,如果你的类名是SalesforceSyncJob,那么它会更短;)
    • @MattConnolly,也使用self.class,如果您碰巧重命名了类,则无需搜索/替换。
    • @GusShortz 是真的。此外,如果有子类,self.class 效果会更好。
    【解决方案2】:

    在继承方面,使用self.class.blah 与使用ClassName.blah 不同。

    class Truck
      def self.default_make
        "mac"
      end
    
      def make1
        self.class.default_make
      end
    
      def make2
        Truck.default_make
      end
    end
    
    
    class BigTruck < Truck
      def self.default_make
        "bigmac"
      end
    end
    
    ruby-1.9.3-p0 :021 > b=BigTruck.new
     => #<BigTruck:0x0000000307f348> 
    ruby-1.9.3-p0 :022 > b.make1
     => "bigmac" 
    ruby-1.9.3-p0 :023 > b.make2
     => "mac" 
    

    【讨论】:

    • 这似乎是对已接受答案的回应,而不是对问题的回答。
    • @zohn - 是的,但在考虑使用什么时,这仍然是有用的上下文。
    • @MattSanders 只是在这些情况下使用评论。
    • @hlcs self.class 保留继承是正确的。即使make1() 是在Truck 中定义的,它也引用了BigTruck 的类方法。
    【解决方案3】:

    要访问实例方法中的类方法,请执行以下操作:

    self.class.default_make
    

    以下是您的问题的替代解决方案:

    class Truck
    
      attr_accessor :make, :year
    
      def self.default_make
        "Toyota"
      end
    
      def make
        @make || self.class.default_make
      end
    
      def initialize(make=nil, year=nil)
        self.year, self.make = year, make
      end
    end
    

    现在让我们使用我们的类:

    t = Truck.new("Honda", 2000)
    t.make
    # => "Honda"
    t.year
    # => "2000"
    
    t = Truck.new
    t.make
    # => "Toyota"
    t.year
    # => nil
    

    【讨论】:

    【解决方案4】:

    如果您有权访问委托方法,则可以这样做:

    [20] pry(main)> class Foo
    [20] pry(main)*   def self.bar
    [20] pry(main)*     "foo bar"
    [20] pry(main)*   end  
    [20] pry(main)*   delegate :bar, to: 'self.class'
    [20] pry(main)* end  
    => [:bar]
    [21] pry(main)> Foo.new.bar
    => "foo bar"
    [22] pry(main)> Foo.bar
    => "foo bar"
    

    或者,如果您有超过一两个方法要委托给类和实例,则可能更简洁:

    [1] pry(main)> class Foo
    [1] pry(main)*   module AvailableToClassAndInstance
    [1] pry(main)*     def bar
    [1] pry(main)*       "foo bar"
    [1] pry(main)*     end  
    [1] pry(main)*   end  
    [1] pry(main)*   include AvailableToClassAndInstance
    [1] pry(main)*   extend AvailableToClassAndInstance
    [1] pry(main)* end  
    => Foo
    [2] pry(main)> Foo.new.bar
    => "foo bar"
    [3] pry(main)> Foo.bar
    => "foo bar"
    

    请注意:

    不要随便delegate 任何不会将状态更改为类和实例的东西,因为你会开始遇到奇怪的名称冲突问题。谨慎地执行此操作,并且仅在您检查没有其他内容被压扁之后。

    【讨论】:

      【解决方案5】:
      self.class.default_make
      

      【讨论】:

        【解决方案6】:

        你做对了。类方法(类似于 C++ 或 Java 中的“静态”方法)不是实例的一部分,因此必须直接引用它们。

        请注意,在您的示例中,最好将“default_make”设置为常规方法:

        #!/usr/bin/ruby
        
        class Truck
            def default_make
                # Class method.
                "mac"
            end
        
            def initialize
                # Instance method.
                puts default_make  # gets the default via the class's method.
            end
        end
        
        myTruck = Truck.new()
        

        类方法对于使用类的实用程序类型函数更有用。例如:

        #!/usr/bin/ruby
        
        class Truck
            attr_accessor :make
        
            def default_make
                # Class method.
                "mac"
            end
        
            def self.buildTrucks(make, count)
                truckArray = []
        
                (1..count).each do
                    truckArray << Truck.new(make)
                end
        
                return truckArray
            end
        
            def initialize(make = nil)
                if( make == nil )
                    @make = default_make()
                else
                    @make = make
                end
            end
        end
        
        myTrucks = Truck.buildTrucks("Yotota", 4)
        
        myTrucks.each do |truck|
            puts truck.make
        end
        

        【讨论】:

        • 我不同意 default_make 应该是一个实例方法。即使这些示例更简单,它也不是正确的语义 - 默认是类的产品,而不是属于该类的对象。
        • @Peter 您愿意用更简单的术语解释一下吗?我只是在学习 Ruby,Maha 的答案对我来说似乎很完美。
        • @MarlenT.B.回想起来,我不确定这里有太多要学习的东西——我只是在争论放置该方法的最佳位置,而且我不再那么强烈地相信自己的论点了! :)
        • 我也不同意。某物是否是类方法与“效用”无关。它是关于该方法在概念上是否适用于类或该类的对象。例如,每辆卡车都有不同的序列号,因此 serial_number 是一个实例方法(具有相应的实例变量)。另一方面,vehicle_type(返回“卡车”)应该是一个类方法,因为这是所有卡车的属性,而不是特定的卡车
        【解决方案7】:

        还有一个:

        class Truck
          def self.default_make
            "mac"
          end
        
          attr_reader :make
        
          private define_method :default_make, &method(:default_make)
        
          def initialize(make = default_make)
            @make = make
          end
        end
        
        puts Truck.new.make # => mac
        

        【讨论】:

          【解决方案8】:

          这是一种关于如何实现_class 方法的方法,该方法在这种情况下用作self.class。注意:不要在生产代码中使用它,这是为了兴趣:)

          发件人:Can you eval code in the context of a caller in Ruby?http://rubychallenger.blogspot.com.au/2011/07/caller-binding.html

          # Rabid monkey-patch for Object
          require 'continuation' if RUBY_VERSION >= '1.9.0'
          class Object
            def __; eval 'self.class', caller_binding; end
            alias :_class :__
            def caller_binding
              cc = nil; count = 0
              set_trace_func lambda { |event, file, lineno, id, binding, klass|
                if count == 2
                  set_trace_func nil
                  cc.call binding
                elsif event == "return"
                  count += 1
                end
              }
              return callcc { |cont| cc = cont }
            end
          end
          
          # Now we have awesome
          def Tiger
            def roar
              # self.class.roar
              __.roar
              # or, even
              _class.roar
            end
            def self.roar
              # TODO: tigerness
            end
          end
          

          也许正确的答案是为 Ruby 提交补丁 :)

          【讨论】:

            【解决方案9】:

            类似你的问题,你可以使用:

            class Truck
              def default_make
                # Do something
              end
            
              def initialize
                super
                self.default_make
              end
            end
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2010-09-30
              • 2015-10-26
              • 2016-07-29
              • 1970-01-01
              • 2012-06-23
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多