Đưa ra một lớp, xem nếu cá thể có phương thức (Ruby)


227

Tôi biết trong Ruby rằng tôi có thể sử dụng respond_to?để kiểm tra xem một đối tượng có một phương thức nhất định không.

Nhưng, với lớp, làm thế nào tôi có thể kiểm tra xem cá thể có một phương thức nhất định không?

tức là một cái gì đó như

Foo.new.respond_to?(:bar)

Nhưng tôi cảm thấy như có một cách tốt hơn là bắt đầu một ví dụ mới.

Câu trả lời:


364

Tôi không biết tại sao mọi người lại khuyên bạn nên sử dụng instance_methodsinclude?khi method_defined?nào thì công việc.

class Test
  def hello; end
end

Test.method_defined? :hello #=> true

GHI CHÚ

Trong trường hợp bạn đến với Ruby từ một ngôn ngữ OO khác HOẶC bạn nghĩ điều đó method_definedcó nghĩa là các phương thức CHỈ mà bạn đã xác định rõ ràng với:

def my_method
end

sau đó đọc nó:

Trong Ruby, một thuộc tính (thuộc tính) trên mô hình của bạn về cơ bản cũng là một phương thức. Vì vậy, method_defined?cũng sẽ trả về true cho các thuộc tính, không chỉ các phương thức.

Ví dụ:

Cho một thể hiện của một lớp có thuộc tính String first_name:

<instance>.first_name.class #=> String

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

first_namecả thuộc tính và phương thức (và chuỗi kiểu String).


9
Phương thức được xác định? là giải pháp tốt nhất vì instance_methods đã thay đổi giữa Ruby 1.8 và 1.9. 1.8 trả về một mảng các chuỗi và 1.9 trả về một mảng các ký hiệu. Phương thức được xác định? lấy một biểu tượng trong cả 1.8 và 1.9.
Jason

2
Chưa kể rằng: instance_methods sẽ tạo ra một mảng mới mỗi cuộc gọi và rằng: bao gồm? sau đó sẽ phải đi bộ mảng để nói. Ngược lại ,: method_d xác định? sẽ truy vấn nội bộ các bảng tra cứu phương thức (một tra cứu băm trong C) và cho bạn biết mà không cần tạo bất kỳ đối tượng mới nào.
Asher

4
Mặc dù có ít nhất một lợi thế khi bạn sử dụng nó như thế này, String.instance_methods(false).include? :freezenhưng đối số sai có thể cho biết chính xác liệu freezephương thức có được định nghĩa trong lớp String hay không. Trong trường hợp này, nó sẽ trả về false vì freezephương thức được kế thừa từ mô-đun Kernel bởi String, nhưng String.method_defined? :freezesẽ luôn trả về true, điều này không giúp được gì nhiều cho mục đích như vậy.
ugoa

1
@Bane bạn đang nhầm lẫn các phương thức trên lớp với các phương thức có sẵn trên ví dụ. method_defined?phải được sử dụng trên lớp (ví dụ Array), nhưng bạn đang cố gắng sử dụng nó trong các trường hợp (ví dụ [])
Horseyguy

@banister Hoàn toàn chính xác. Cảm ơn bạn đã làm rõ, nó có ý nghĩa hoàn hảo khi đọc nó. :) Tôi đã xóa nhận xét của mình để không nhầm lẫn.
Bane

45

Bạn có thể sử dụng method_defined?như sau:

String.method_defined? :upcase # => true

Dễ dàng hơn nhiều, di động và hiệu quả hơn những instance_methods.include?người khác dường như đang đề xuất.

Hãy nhớ rằng bạn sẽ không biết liệu một lớp có phản hồi linh hoạt với một số cuộc gọi hay không method_missing, ví dụ như bằng cách xác định lại respond_to?hoặc kể từ Ruby 1.9.2 bằng cách xác định respond_to_missing?.


2
Lưu ý rằng nếu bạn đã có một ví dụ trong tay và bạn không biết đó là lớp học, bạn có thể làminstance.class.method_defined? :upcase
jaydel

25

Trên thực tế, điều này không hoạt động cho cả Đối tượng và Lớp.

Cái này không

class TestClass
  def methodName
  end
end

Vì vậy, với câu trả lời đã cho, điều này hoạt động:

TestClass.method_defined? :methodName # => TRUE

Dường như không hiệu quả:

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

Vì vậy, tôi sử dụng điều này cho cả các lớp và các đối tượng:

Các lớp học:

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

Các đối tượng:

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

3
Đối với các trường hợp, bạn cũng có thể sử dụng `instance. Class.method_d xác định? '
Luksquil

Nó phụ thuộc vào phiên bản ruby ​​của bạn. Phương pháp này ở trên hoạt động cho tất cả các phiên bản bao gồm 1.8.7.
Alex V

10

Câu trả lời cho " Đưa ra một lớp, xem thể hiện có phương thức (Ruby) " hay hơn không. Rõ ràng Ruby có tích hợp sẵn này và bằng cách nào đó tôi đã bỏ lỡ nó. Câu trả lời của tôi là để tham khảo, bất kể.

Các lớp Ruby đáp ứng với các phương thức instance_methodspublic_instance_methods. Trong Ruby 1.8, cái đầu tiên liệt kê tất cả các tên phương thức cá thể trong một chuỗi các chuỗi và cái thứ hai giới hạn nó với các phương thức công khai. Hành vi thứ hai là những gì bạn rất có thể muốn, vì respond_to?mặc định cũng hạn chế các phương thức công khai.

Foo.public_instance_methods.include?('bar')

Tuy nhiên, trong Ruby 1.9, các phương thức đó trả về các mảng ký hiệu.

Foo.public_instance_methods.include?(:bar)

Nếu bạn dự định làm điều này thường xuyên, bạn có thể muốn gia hạn Module để bao gồm một phương pháp phím tắt. (Việc Modulethay thế điều này có vẻ kỳ quặc thay vì Class, nhưng vì đó là nơi các instance_methodsphương thức sống, tốt nhất nên tuân theo mô hình đó.)

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

Nếu bạn muốn hỗ trợ cả Ruby 1.8 và Ruby 1.9, đó cũng là một nơi thuận tiện để thêm logic để tìm kiếm cả chuỗi và ký hiệu.


1
method_defined?tốt hơn :)
Horseyguy

@banister - oh, của tôi, và bằng cách nào đó tôi đã bỏ lỡ nó! xD Đã tạo +1 sau câu trả lời của @ Marc và thêm một liên kết để đảm bảo rằng nó không bị bỏ lỡ :)
Matchu


3

Không chắc đây có phải là cách tốt nhất không, nhưng bạn luôn có thể làm điều này:

Foo.instance_methods.include? 'bar'


3

Nếu bạn đang kiểm tra xem liệu một đối tượng có thể phản hồi với một loạt các phương thức hay không, bạn có thể làm một cái gì đó như:

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

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

những methods & something.methodssẽ tham gia vào hai mảng trên các yếu tố chung / phù hợp với họ. Something.methods bao gồm tất cả các phương thức bạn đang kiểm tra, nó sẽ là các phương thức bằng nhau. Ví dụ:

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

vì thế

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

Trong tình huống này, bạn muốn sử dụng các ký hiệu, bởi vì khi bạn gọi .method, nó sẽ trả về một mảng các ký hiệu và nếu bạn sử dụng ["my", "methods"], nó sẽ trả về sai.


2

klass.instance_methods.include :method_namehoặc "method_name", tùy thuộc vào phiên bản Ruby tôi nghĩ.


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] 

Hy vọng điều này sẽ giúp bạn!


1

Trong trường hợp của tôi làm việc với ruby ​​2.5.3, các câu sau đây đã hoạt động hoàn hảo:

value = "hello world"

value.methods.include? :upcase

Nó sẽ trả về giá trị boolean đúng hoặc sai.


1

Mặc dù respond_to?sẽ trả về true chỉ cho các phương thức công khai, việc kiểm tra "định nghĩa phương thức" trên một lớp cũng có thể liên quan đến các phương thức riêng tư.

Trên Ruby v2.0 + kiểm tra cả bộ công khai và riêng tư có thể đạt được với

Foo.private_instance_methods.include?(:bar) || Foo.instance_methods.include?(:bar)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.