【问题标题】:How to check if a class already exists in Ruby如何检查一个类是否已经存在于 Ruby 中
【发布时间】:2009-07-27 08:38:32
【问题描述】:

如何检查一个类是否已经存在于 Ruby 中?

我的代码是:

puts "enter the name of the Class to see if it exists"   
nameofclass=gets.chomp  
eval (" #{nameofclass}......  Not sure what to write here")

我正在考虑使用:

eval "#{nameofclass}ancestors.     ....."

【问题讨论】:

    标签: ruby class


    【解决方案1】:

    您可以使用Module.const_get 来获取字符串引用的常量。它将返回常量(通常类由常量引用)。然后您可以检查该常量是否是一个类。

    我会按照这些思路做一些事情:

    def class_exists?(class_name)
      klass = Module.const_get(class_name)
      return klass.is_a?(Class)
    rescue NameError
      return false
    end
    

    另外,如果可能的话,我在接受用户输入时总是避免使用eval;我怀疑这将用于任何严重的应用程序,但值得注意安全风险。

    【讨论】:

    • 对我来说,如果命名的东西也是一个模块就好了。使用klass.is_a?(Class) || klass.is_a?(Module)。命名空间限定名称与此方法一起使用。例如class_exists?('Delayed::Worker') 非常好。
    【解决方案2】:

    也许你可以通过定义来做到这一点?

    例如:

    if defined?(MyClassName) == 'constant' && MyClassName.class == Class  
       puts "its a class" 
    end
    

    注意:需要进行Class检查,例如:

    Hello = 1 
    puts defined?(Hello) == 'constant' # returns true
    

    回答原问题:

    puts "enter the name of the Class to see if it exists"
    nameofclass=gets.chomp
    eval("defined?(#{nameofclass}) == 'constant' and #{nameofclass}.class == Class")
    

    【讨论】:

    • 这是最好的方法。在 gems 中的很多情况下,你也需要做这样的事情。定义? ::导轨
    • defined?(MyClassName) == 'constant' && MyClassName.is_a?(Class) 看起来更直观。
    • 为什么defined? Object.const_get("array".capitalize)返回“方法”,而我运行defined? Array返回“常量”?
    • 只是最近出现的一些问题的警告,定义了吗?如果 MyClassName 不存在,将抛出错误。至少在当前版本的 Ruby 中。有关更多信息,您可以查看文档:(ruby-doc.org/docs/keywords/1.9/Object.html#method-i-defined-3F)
    • ``` irb(main):003:0> 定义了吗? TestingTesting::Testing => nil irb(main):004:0> RUBY_VERSION => "2.6.1" ```
    【解决方案3】:

    如果您通过调用Module#const_defined?("SomeClass") 在特定范围内查找常量,则可以避免必须从Module.const_get 拯救NameError。

    调用它的常见范围是 Object,例如:Object.const_defined?("User")

    参见:“Module”。

    【讨论】:

    • 只有在类已经加载时才会返回 true。 >> Object.const_defined?("MyClass") # false >> MyClass # MyClass >> Object.const_defined?("MyClass") # true 这至少在 Rails 控制台中是这样的,YMMV。
    【解决方案4】:
    defined?(DatabaseCleaner) # => nil
    require 'database_cleaner'
    defined?(DatabaseCleaner) # => constant
    

    【讨论】:

      【解决方案5】:

      类名是常量。您可以使用defined? 方法查看是否定义了常量。

      defined?(String)    # => "constant"
      defined?(Undefined) # => nil
      

      如果您有兴趣,可以阅读有关defined? 工作原理的更多信息。

      【讨论】:

      • 这对定义不起作用?不过,“字符串”确实是个问题。
      【解决方案6】:
      Kernel.const_defined?("Fixnum") # => true
      

      【讨论】:

      • 只有在类已经加载$ rails c test Loading test environment (Rails 4.2.5.1) 2.2.1 :001 > Kernel.const_defined?('ApiProvider') => false 2.2.1 :002 > ApiProvider => ApiProvider 2.2.1 :003 > Kernel.const_defined?('ApiProvider') => true 时才会返回true
      【解决方案7】:

      这里有一个更简洁的版本:

      def class_exists?(class_name)
        eval("defined?(#{class_name}) && #{class_name}.is_a?(Class)") == true
      end
      
      class_name = "Blorp"
      class_exists?(class_name)
      => false
      
      class_name = "String"
      class_exists?(class_name)
      => true
      

      【讨论】:

        【解决方案8】:

        我有时会做一些事情来解决这个问题。您可以像这样将以下方法添加到 String 类中:

        class String
            def to_class
                my_const = Kernel.const_get(self)
                my_const.is_a?(Class) ? my_const : nil
            rescue NameError 
                nil
            end
        
            def is_a_defined_class?
                true if self.to_class
            rescue NameError
                false
            end
        end
        

        然后:

        'String'.to_class
        => String
        'unicorn'.to_class
        => nil
        'puppy'.is_a_defined_class?
        => false
        'Fixnum'.is_a_defined_class?
        => true
        

        【讨论】:

        • 这是一种非常酷的方法,尤其是当您以字符串的形式(例如 Rails)处理用户输入时。感谢这个好主意!
        【解决方案9】:

        在一行中,我会写:

        !!Module.const_get(nameofclass) rescue false
        

        仅当给定的 nameofclass 属于已定义的类时才会返回 true

        【讨论】:

        • 对于 Module 中的任何常量(可能是全部,我不知道),这将返回 true,而不仅仅是类。 Bla="some string" ; !!Module.const_get(:Bla) rescue false 返回真。
        【解决方案10】:

        我用它来查看是否在运行时加载了一个类:

        def class_exists?(class_name)
          ObjectSpace.each_object(Class) {|c| return true if c.to_s == class_name }
          false
        end
        

        【讨论】:

        • 我使用了这个方法,因为在我的场景中我不知道这个常量之前是否已经创建过,重要的是它只与 Class 对象进行比较。能够只传递一个字符串是完美的。感谢您的分享,您帮了我很多忙,非常感谢。
        【解决方案11】:

        以上答案都不适合我,可能是因为我的代码位于子模块的范围内。

        我决定在我的模块中创建一个class_exists? 方法,使用在Fred Wilmore's reply to "How do I check if a class is defined?" 中找到的代码,最后停止了诅咒。

        def class_exists?(name)
           name.constantize.is_a?(Class) rescue false # rubocop:disable Style/RescueModifier
        end
        

        完整代码,好奇的朋友:

        module Some
          module Thing
            def self.build(object)
              name = "Some::Thing::#{object.class.name}"
              class_exists?(name) ? name.constantize.new(object) : Base.new(object)
            end
        
            def self.class_exists?(name)
              name.constantize.is_a?(Class) rescue false # rubocop:disable Style/RescueModifier
            end
        
            private_class_method :class_exists?
          end
        end
        

        我将它用作一个工厂,它根据作为参数传递的对象的类来构建对象:

        Some::Thing.build(something)
        => # A Some::Thing::Base object
        Some::Thing.build(something_else)
        => # Another object, which inherits from Some::Thing::Base
        

        【讨论】:

          【解决方案12】:

          如果它是一个简单的独立类,例如

          class Foo
          end
          

          那么我们可以使用Object.const_defined?("Foo")。而且,如果您的课程在任何模块中,例如

          module Bar
            class Foo
            end
          end
          

          那么我们可以使用Module.const_get("Bar::Foo")

          请注意,如果Module.const_get('Bar::Foo') 没有找到该类,那么它将引发异常。而Object.const_defined?('Foo') 将返回truefalse

          【讨论】:

            【解决方案13】:

            如果类没有加载,我假设你会采取一些行动。

            如果您的意思是需要一个文件,为什么不检查require 的输出?

            require 'already/loaded'  
            => false
            

            【讨论】:

              【解决方案14】:

              如果你想要打包的东西,finishing_moves gem 会添加一个class_exists? 方法。

              class_exists? :Symbol
              # => true
              class_exists? :Rails
              # => true in a Rails app
              class_exists? :NonexistentClass
              # => false
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2015-03-10
                • 2011-04-17
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2021-04-20
                • 2021-10-03
                相关资源
                最近更新 更多