Ruby's hai dấu hai chấm `::` là gì?


426

Đôi đại tràng này là ::gì? Ví dụ Foo::Bar.

Tôi tìm thấy một định nghĩa :

Đây ::là một toán tử đơn nguyên cho phép: các hằng, các phương thức cá thể và các phương thức lớp được định nghĩa trong một lớp hoặc mô-đun, được truy cập từ bất cứ đâu bên ngoài lớp hoặc mô-đun.

Phạm vi là gì (riêng tư, được bảo vệ) nếu bạn chỉ có thể sử dụng :: để phơi bày bất cứ điều gì?


175
Vì lợi ích của các nhân viên Google trong tương lai, nếu bạn đang cố gắng tìm kiếm một biểu tượng, hãy thử Symbolhound.com
Andrew Grimm



6
Chúc lành cho bạn, @AndrewGrimm. Đó là điều tốt nhất tôi đã thấy trong tuần này.
abeger

Câu trả lời:


381

::về cơ bản là một toán tử phân giải không gian tên. Nó cho phép bạn truy cập các mục trong các mô-đun hoặc các mục cấp độ trong các lớp. Ví dụ: giả sử bạn đã có thiết lập này:

module SomeModule
    module InnerModule
        class MyClass
            CONSTANT = 4
        end
    end
end

Bạn có thể truy cập CONSTANTtừ bên ngoài mô-đun như SomeModule::InnerModule::MyClass::CONSTANT.

Nó không ảnh hưởng đến các phương thức cá thể được định nghĩa trên một lớp, vì bạn truy cập vào các phương thức có cú pháp khác (dấu chấm .).

Lưu ý liên quan: Nếu bạn muốn quay lại không gian tên cấp cao nhất, hãy làm điều này: :: Một số mô hình - Benjamin Oakes


5
Trong C #, ví dụ, có. Mặt khác, C ++ (và Ruby) sử dụng ::cho độ phân giải không gian tên, chẳng hạn nhưstd::cout << "Hello World!";
Jerry Fernholz

142
Lưu ý liên quan: Nếu bạn muốn quay lại không gian tên cấp cao nhất, hãy làm điều này: ::SomeModule
Benjamin Oakes

5
@Benjamin Các dấu hai chấm hàng đầu được ngụ ý, trừ khi tôi tình cờ có một số Mô-đun bên trong một mô-đun khác và tôi muốn lấy một cấp cao nhất thay thế, đúng không?
Jo Liss

7
@Jo Vâng. Nó có thể hữu ích nếu bạn muốn đảm bảo rằng bạn đang đề cập đến một hằng số ở không gian tên cấp cao nhất hoặc một hằng có cùng tên trong một mô-đun khác (ví dụ :: someOtherModule :: ClassMethods).
Benjamin Oakes

2
Điều này rất giống với toán hạng phạm vi của C ++
lkahtz

111

Ví dụ đơn giản này minh họa nó:

MR_COUNT = 0        # constant defined on main Object class
module Foo
  MR_COUNT = 0
  ::MR_COUNT = 1    # set global count to 1
  MR_COUNT = 2      # set local count to 2
end

puts MR_COUNT       # this is the global constant: 1
puts Foo::MR_COUNT  # this is the local constant: 2

Lấy từ http://www.tutorialspoint.com/ruby/ruby_operators.htmlm


đây là những gì gây ra cảnh báo mặc dù. Có cách nào để trốn tránh cảnh báo?
NullVoxPopuli

3
@NullVoxPopuli Nói chung điều chỉnh hằng là một điều thực sự tồi tệ nhưng nếu bạn ví dụ muốn thay đổi một hằng số trong một viên ngọc nặng bằng văn bản và không muốn ngã ba nó, nó có thể được thực hiện bằng cách sử dụng .send (: remove_const) để mô-đun mà định nghĩa nó, sau đó xác định lại hằng số.
BookOfGreg

71

::Cho phép bạn truy cập vào một hằng, mô-đun hoặc lớp được xác định bên trong một lớp hoặc mô-đun khác. Nó được sử dụng để cung cấp các không gian tên để các tên phương thức và lớp không xung đột với các lớp khác bởi các tác giả khác nhau.

Khi bạn nhìn thấy ActiveRecord::Basetrong Rails, điều đó có nghĩa là Rails có một cái gì đó như

module ActiveRecord
  class Base
  end
end

tức là một lớp được gọi Basebên trong một mô-đun ActiveRecordmà sau đó được tham chiếu là ActiveRecord::Base(bạn có thể tìm thấy điều này trong nguồn Rails trong activerecord-nnn / lib / active_record / base.rb)

Một cách sử dụng phổ biến của :: là để truy cập các hằng số được xác định trong các mô-đun, vd

module Math
  PI = 3.141 # ...
end

puts Math::PI

Các ::nhà điều hành không cho phép bạn để tầm nhìn bypass các phương pháp đánh dầu là private hoặc protected.


7
Vì vậy, nếu có class MyClass < ActiveRecord::Base, điều đó có nghĩa là MyClass chỉ kế thừa các phương thức từ cơ sở lớp chứ không phải bất cứ thứ gì bên trong mô-đun ActiveRecord?
Charlie Parker

2
Tại sao nên sử dụng dấu hai chấm đặc biệt cho độ phân giải không gian tên này thay vì sử dụng dấu "." cho điều này quá? Bối cảnh và viết hoa sẽ ngăn ngừa sự nhầm lẫn về ý nghĩa ngay cả khi chúng ta đang sử dụng ".", Phải không?
Giô-na

3
@Jonah có một số trường hợp sẽ mơ hồ. ví dụ xem xét class Foo; Baz = 42; def self.Baz; "Baz method!"; end; end(hoàn toàn hợp lệ) Foo::Baz # => 42Foo.Baz # => "Baz method!". Lưu ý rằng Foo::Baz()(với dấu ngoặc đơn) cũng sẽ gọi phương thức mặc dù.
mikej

3
Vì vậy, trường hợp sử dụng nó giải quyết nó khả năng có hằng số lớp và một phương thức lớp có cùng tên chính xác không? Điều đó dường như không phải là một lập luận mạnh mẽ ủng hộ tính năng này. Cá nhân tôi thà mất khả năng đó (dù sao cũng có vẻ như rắc rối), mất dấu hai chấm và sử dụng "." cho không gian tên quá .... Có thể có trường hợp sử dụng bổ sung mà nó giải quyết?
Giô-na

26

Phạm vi nào là tốt (riêng tư, được bảo vệ) nếu bạn chỉ có thể sử dụng :: để lộ bất cứ điều gì?

Trong Ruby, mọi thứ đều được phơi bày và mọi thứ có thể được sửa đổi từ bất kỳ nơi nào khác.

Nếu bạn lo lắng về thực tế rằng các lớp có thể được thay đổi từ bên ngoài "định nghĩa lớp", thì Ruby có thể không dành cho bạn.

Mặt khác, nếu bạn cảm thấy thất vọng vì các lớp của Java bị khóa, thì Ruby có lẽ là thứ bạn đang tìm kiếm.


1
Tôi đã nghe một số người theo chủ nghĩa ruby ​​nói rằng các biến thể hiện không bị lộ, thậm chí attr_accessorchỉ tạo ra các phương thức sửa đổi biến. (Sau đó, một lần nữa instance_eval)
Andrew Grimm

4
Đúng, có instance_eval. Nhưng cũng có instance_variable_getinstance_variable_set. Ruby chỉ là quá năng động cho các ràng buộc.
yfeldblum

12

Thêm vào các câu trả lời trước đó, Ruby hợp lệ để sử dụng ::để truy cập các phương thức cá thể. Tất cả những điều sau đây là hợp lệ:

MyClass::new::instance_method
MyClass::new.instance_method
MyClass.new::instance_method
MyClass.new.instance_method

Theo thông lệ tốt nhất, tôi tin rằng chỉ có cách cuối cùng được khuyến nghị.


11

Không, nó không phải là để truy cập mọi phương thức, nó là toán tử "độ phân giải", nghĩa là bạn sử dụng nó để giải quyết phạm vi (hoặc vị trí bạn có thể nói) của một biểu tượng không đổi / tĩnh.

Ví dụ, trong dòng đầu tiên của bạn, Rails sử dụng nó để tìm lớp Base bên trong ActiveRecord.Module, trong lớp thứ hai của bạn, nó được sử dụng để định vị phương thức lớp (tĩnh) của lớp Routes, v.v., v.v.

Nó không được sử dụng để phơi bày bất cứ thứ gì, nó được sử dụng để "định vị" các thứ xung quanh phạm vi của bạn.

http://en.wikipedia.org/wiki/Scope_resolution_operator


bởi "(tĩnh)" bạn có nghĩa là "(vẽ)"?!?
Meltemi

8

Đáng ngạc nhiên, tất cả 10 câu trả lời ở đây nói cùng một điều. '::' là toán tử phân giải không gian tên và đúng vậy. Nhưng có một vấn đề mà bạn phải nhận ra về toán tử phân giải không gian tên khi nói đến thuật toán tra cứu liên tục . Khi Matz phác họa trong cuốn sách của mình, 'Ngôn ngữ lập trình Ruby', việc tra cứu liên tục có nhiều bước. Đầu tiên, nó tìm kiếm một hằng số trong phạm vi từ vựng trong đó hằng số được tham chiếu. Nếu nó không tìm thấy hằng số trong phạm vi từ vựng, thì nó sẽ tìm kiếm hệ thống phân cấp thừa kế . Do thuật toán tra cứu liên tục này, bên dưới chúng tôi nhận được kết quả mong đợi:

module A
  module B
      PI = 3.14
      module C
        class E
          PI = 3.15
        end
        class F < E
          def get_pi
            puts PI
          end
        end
      end
  end
end
f = A::B::C::F.new
f.get_pi
> 3.14

Trong khi F kế thừa từ E, mô-đun B nằm trong phạm vi từ vựng của F. Do đó, các thể hiện F sẽ đề cập đến hằng số PI được xác định trong mô-đun B. Bây giờ nếu mô-đun B không xác định PI, thì các thể hiện F sẽ đề cập đến PI hằng số được định nghĩa trong siêu lớp E.

Nhưng điều gì sẽ xảy ra nếu chúng ta sử dụng '::' thay vì các mô-đun lồng nhau? Chúng ta sẽ nhận được kết quả tương tự? Không!

Bằng cách sử dụng toán tử phân giải không gian tên khi xác định các mô đun lồng nhau, các mô đun và các lớp lồng nhau không còn nằm trong phạm vi từ vựng của các mô đun bên ngoài của chúng. Như bạn có thể thấy bên dưới, PI được định nghĩa trong A :: B không nằm trong phạm vi từ vựng của A :: B :: C :: D và do đó chúng ta nhận được hằng số chưa được khởi tạo khi cố gắng tham chiếu PI trong phương thức ví dụ get_pi:

module A
end

module A::B
  PI = 3.14
end

module A::B::C
  class D
    def get_pi
      puts PI
    end
  end
end
d = A::B::C::D.new
d.get_pi
NameError: uninitialized constant A::B::C::D::PI
Did you mean?  A::B::PI

4

Đó là tất cả về việc ngăn chặn các định nghĩa xung đột với mã khác được liên kết đến dự án của bạn. Nó có nghĩa là bạn có thể giữ mọi thứ riêng biệt.

Ví dụ: bạn có thể có một phương thức gọi là "chạy" trong mã của mình và bạn vẫn có thể gọi phương thức của mình thay vì phương thức "chạy" đã được xác định trong một số thư viện khác mà bạn đã liên kết.


3
module Amimal
      module Herbivorous
            EATER="plants" 
      end
end

Amimal::Herbivorous::EATER => "plants"

:: Được sử dụng để tạo ra một phạm vi. Để truy cập Constant EATER từ 2 mô-đun, chúng ta cần phạm vi các mô-đun để đạt tới hằng số


3

Ruby on rails sử dụng ::cho độ phân giải không gian tên.

class User < ActiveRecord::Base

  VIDEOS_COUNT = 10
  Languages = { "English" => "en", "Spanish" => "es", "Mandarin Chinese" => "cn"}

end

Để dùng nó :

User::VIDEOS_COUNT
User::Languages
User::Languages.values_at("Spanish") => "en"

Ngoài ra, cách sử dụng khác là: Khi sử dụng các tuyến đường lồng nhau

OmniauthCallbacksController được định nghĩa dưới người dùng.

Và định tuyến là:

devise_for :users, controllers: {omniauth_callbacks: "users/omniauth_callbacks"}


class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController

end
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.