我如何检查一个类是否被定义?

如何将string转换为类名,但只有当该类已经存在?

如果琥珀已经是一个类,我可以通过以下方式从一个string中获得:

Object.const_get("Amber") 

或(在Rails中)

 "Amber".constantize 

但是其中的任何一个都会因为NameError: uninitialized constant Amber而失败NameError: uninitialized constant Amber如果琥珀色还不是一个类,那么就是NameError: uninitialized constant Amber

我的第一个想法是使用defined? 方法,但它不区分已经存在的类和不存在的类:

 >> defined?("Object".constantize) => "method" >> defined?("AClassNameThatCouldNotPossiblyExist".constantize) => "method" 

那么在我尝试转换它之前,如何testing一个string是否可以命名一个类呢? (好吧,如何begin / rescue块赶上NameError错误?太丑了?我同意…)

怎么样const_defined?

在Rails中记住,在开发模式下会自动加载,所以在testing时可能会非常棘手:

 >> Object.const_defined?('Account') => false >> Account => Account(id: integer, username: string, google_api_key: string, created_at: datetime, updated_at: datetime, is_active: boolean, randomize_search_results: boolean, contact_url: string, hide_featured_results: boolean, paginate_search_results: boolean) >> Object.const_defined?('Account') => true 

在铁轨中,这非常简单:

 amber = "Amber".constantize rescue nil if amber # nil result in false # your code here end 

受上述@ ctcherry的回应的启发,这里有一个“安全类方法send”,其中class_name是一个string。 如果class_name没有命名一个类,它返回nil。

 def class_send(class_name, method, *args) Object.const_defined?(class_name) ? Object.const_get(class_name).send(method, *args) : nil end 

一个更安全的版本只有在class_name响应时才会调用method

 def class_send(class_name, method, *args) return nil unless Object.const_defined?(class_name) c = Object.const_get(class_name) c.respond_to?(method) ? c.send(method, *args) : nil end 

它会出现所有的答案使用Object.const_defined? 方法有缺陷。 如果有问题的类尚未加载,由于延迟加载,则断言将失败。 实现这一目标的唯一方法就是这样:

  validate :adapter_exists def adapter_exists # cannot use const_defined because of lazy loading it seems Object.const_get("Irs::#{adapter_name}") rescue NameError => e errors.add(:adapter_name, 'does not have an IrsAdapter') end 

另一种方法,如果你想获得课程。 如果类没有定义,将会返回nil,所以你不必捕获exception。

 class String def to_class(class_name) begin class_name = class_name.classify (optional bonus feature if using Rails) Object.const_get(class_name) rescue # swallow as we want to return nil end end end > 'Article'.to_class class Article > 'NoSuchThing'.to_class nil # use it to check if defined > puts 'Hello yes this is class' if 'Article'.to_class Hello yes this is class 

我创build了一个validation器来testing一个string是否是一个有效的类名(或逗号分隔的有效类名):

 class ClassValidator < ActiveModel::EachValidator def validate_each(record,attribute,value) unless value.split(',').map { |s| s.strip.constantize.is_a?(Class) rescue false }.all? record.errors.add attribute, 'must be a valid Ruby class name (comma-separated list allowed)' end end end