Bir sınıf verildiğinde, örneğin yöntemi (Ruby) olup olmadığına bakın


227

Ruby'de respond_to?bir nesnenin belirli bir yöntemi olup olmadığını kontrol etmek için kullanabileceğimi biliyorum .

Ancak, sınıf göz önüne alındığında, örneğin belirli bir yöntemi olup olmadığını nasıl kontrol edebilirim?

yani, benzeri bir şey

Foo.new.respond_to?(:bar)

Fakat yeni bir örnek oluşturmadan daha iyi bir yol olması gerektiğini hissediyorum.

Yanıtlar:


364

Herkesin kullanıyor olmalıdır düşündüren neden bilmiyorum instance_methodsve include?ne zaman method_defined?iş yapar.

class Test
  def hello; end
end

Test.method_defined? :hello #=> true

NOT

Ruby'ye başka bir OO dilinden geliyorsanız VEYA bunun method_definedSADECE açıkça tanımladığınız yöntemler anlamına geldiğini düşünüyorsunuz :

def my_method
end

sonra bunu okuyun:

Ruby'de, modelinizdeki bir özellik (özellik) de temel olarak bir yöntemdir. Bu nedenle method_defined?, yalnızca yöntemler için değil , özellikler için de doğru döner .

Örneğin:

String özniteliğine sahip bir sınıf örneği verildiğinde first_name:

<instance>.first_name.class #=> String

<instance>.class.method_defined?(:first_name) #=> true

çünkü first_namehem bir öznitelik hem de bir yöntemdir (ve String türünde bir dize).


9
method_defined? example_methods Ruby 1.8 ile 1.9 arasında değiştiği için en iyi çözümdür. 1.8 bir dizi dizeyi ve 1.9 bir dizi diziyi döndürür. method_defined? hem 1.8 hem de 1.9'da bir sembol alır.
Jason

2
Şunu da belirtmemek gerekirse: instance_methods her çağrıda yeni bir dizi yaratacaktır ve şunları içerir: include? sonra anlatmak için dizi yürümek zorunda kalacak. Aksine,: method_defined? yöntem arama tablolarını (C'deki bir karma arama) dahili olarak sorgulayacak ve yeni nesneler oluşturmadan size bildirecektir.
Asher

4
Böyle kullandığınız zaman en az bir avantajı varken, String.instance_methods(false).include? :freeze, yanlış argüman olmadığını tam olarak söyleyebiliyor freezeyöntem String sınıfında tanımlanmış veya edilmez. Bu durumda, freezeyöntem String tarafından Çekirdek modülünden devralındığı için false döndürür , ancak String.method_defined? :freezeher zaman true değerini döndürür, bu da böyle bir amaca çok yardımcı olamaz.
ugoa

1
@ Sınıftaki yöntemleri örnekte bulunan yöntemlerle karıştırıyorsunuz. method_defined?sınıfta kullanılmalıdır (örneğin Array), ancak örneklerde (örneğin []) kullanmaya çalışıyorsunuz
horseyguy

@banister Kesinlikle doğru. Açıklık için teşekkür ederim, okumak için mükemmel bir mantıklı. :) Ben karıştırmamak için yorum kaldırdım.

45

Aşağıdaki gibi kullanabilirsiniz method_defined?:

String.method_defined? :upcase # => true

instance_methods.include?Herkesin önerdiğinden çok daha kolay, taşınabilir ve verimli .

Bir sınıfın bazı çağrılara method_missing, örneğin yeniden tanımlayarak respond_to?veya Ruby 1.9.2'den beri tanımlayarak dinamik olarak yanıt verip vermediğini bilmeyeceğinizi unutmayın respond_to_missing?.


2
Elinizde bir örnek varsa ve sınıf olduğunu bilmiyorsanız şunları yapabilirsinizinstance.class.method_defined? :upcase
jaydel

25

Aslında bu hem Nesneler hem de Sınıflar için geçerli değildir.

Bu yapar:

class TestClass
  def methodName
  end
end

Verilen cevapla bu işe yarar:

TestClass.method_defined? :methodName # => TRUE

Ancak bu işe yaramaz:

t = TestClass.new
t.method_defined? : methodName  # => ERROR!

Bu yüzden hem sınıflar hem de nesneler için kullanıyorum:

Sınıflar:

TestClass.methods.include? 'methodName'  # => TRUE

Nesneler:

t = TestClass.new
t.methods.include? 'methodName'  # => TRUE

3
Örnekler için ayrıca `instance.class.method_defined? '
Lüks

Ruby sürümünüze bağlıdır. Yukarıdaki bu yöntem 1.8.7 dahil tüm sürümler için geçerlidir.
Alex V

10

" Bir sınıf verildiğinde, örneğin yöntemi (Ruby) olup olmadığına bakın " cevabı daha iyidir. Görünüşe göre Ruby bu yerleşik ve ben bir şekilde özledim. Ne olursa olsun cevabım referans için bırakıldı.

Yakut sınıfları yöntemleri cevap instance_methodsve public_instance_methods. Ruby 1.8'de birincisi, bir dizgideki tüm örnek yöntemi adlarını listeler ve ikincisi onu genel yöntemlerle sınırlar. İkinci davranış, büyük olasılıkla isteyeceğiniz şeydir, çünkü respond_to?varsayılan olarak kendisini genel yöntemlerle sınırlar.

Foo.public_instance_methods.include?('bar')

Ancak Ruby 1.9'da, bu yöntemler sembol dizilerini döndürür.

Foo.public_instance_methods.include?(:bar)

Bunu sık sık yapmayı planlıyorsanız, Modulebir kısayol yöntemi içerecek şekilde genişletmek isteyebilirsiniz . (Bunu Modulebunun yerine atamak tuhaf görünebilir Class, ancak instance_methodsyöntemlerin yaşadığı yer olduğu için , bu modelle uyumlu kalmak en iyisidir.)

class Module
  def instance_respond_to?(method_name)
    public_instance_methods.include?(method_name)
  end
end

Hem Ruby 1.8 hem de Ruby 1.9'u desteklemek istiyorsanız, hem dizeleri hem de sembolleri aramak için mantığı eklemek için uygun bir yer olacaktır.


1
method_defined?is better :)
horseyguy

@banister - oh, benim ve ben bir şekilde özledim! xD @ Marc'ın cevabının arkasına +1 attı ve kaçırılmadığından emin olmak için bir bağlantı ekledi :)
Matchu


3

Bunun en iyi yol olup olmadığından emin değilim, ancak her zaman bunu yapabilirsiniz:

Foo.instance_methods.include? 'bar'


3

Bir nesnenin bir dizi yönteme yanıt verip vermediğini kontrol ediyorsanız, aşağıdakileri yapabilirsiniz:

methods = [:valid?, :chase, :test]

def has_methods?(something, methods)
  methods & something.methods == methods
end

methods & something.methodsortak / eşleştirme elemanları üzerinde iki diziyi katılacak. something.methods kontrol ettiğiniz tüm yöntemleri içerir, eşit yöntemlere sahip olacaktır. Örneğin:

[1,2] & [1,2,3,4,5]
==> [1,2]

yani

[1,2] & [1,2,3,4,5] == [1,2]
==> true

Bu durumda, sembolleri kullanmak istersiniz, çünkü .methods öğesini çağırdığınızda, bir dizi sembol döndürür ve kullanırsanız ["my", "methods"]false döndürür.


2

klass.instance_methods.include :method_nameya "method_name"da Ruby versiyonuna bağlı olarak bence.


2
class Foo
 def self.fclass_method
 end
 def finstance_method
 end
end

foo_obj = Foo.new
foo_obj.class.methods(false)
=> [:fclass_method] 

foo_obj.class.instance_methods(false)
=> [:fclass_method] 

Umarım bu size yardımcı olur!


1

Benim durumumda ruby ​​2.5.3 ile çalışmak aşağıdaki cümleler mükemmel çalıştı:

value = "hello world"

value.methods.include? :upcase

Doğru veya yanlış bir boole değeri döndürür.


1

Ederken respond_to?aynı zamanda özel yöntemlerle ilgilendirmeyen olabilir bir sınıf üzerinde "yöntemi tanımı" denetlenirken, sadece kamu yöntemleri için geçerlidir dönecektir.

Ruby v2.0 + 'da hem genel hem de özel kümeleri kontrol etmek

Foo.private_instance_methods.include?(:bar) || Foo.instance_methods.include?(:bar)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.