什么是Ruby的双冒号`::`?

问题描述 投票:392回答:10

什么是这个双结肠::?例如。 Foo::Bar

我发现了一个definition

::是一元运算符,允许:从类或模块外的任何位置访问类或模块中定义的常量,实例方法和类方法。

如果您可以使用::来暴露任何东西,范围(私人,受保护)有什么用?

ruby syntax operators
10个回答
357
投票

::基本上是命名空间解析运算符。它允许您访问模块中的项目或类中的类级别项目。例如,假设你有这个设置:

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

您可以从模块外部访问CONSTANT作为SomeModule::InnerModule::MyClass::CONSTANT

它不会影响在类上定义的实例方法,因为您可以访问具有不同语法的那些(点.)。

相关说明:如果要返回顶级命名空间,请执行以下操作::: SomeModule - Benjamin Oakes


2
投票
module Amimal
      module Herbivorous
            EATER="plants" 
      end
end

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

::用于创建范围。为了从2个模块访问Constant EATER,我们需要将模块的范围扩展到常量


106
投票

这个简单的例子说明了它:

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
puts Foo::MR_COUNT  # this is the local "Foo" constant

取自http://www.tutorialspoint.com/ruby/ruby_operators.htm


70
投票

::允许您访问在另一个类或模块中定义的常量,模块或类。它用于提供名称空间,以便方法和类名称不会与不同作者的其他类冲突。

当你在Rails中看到ActiveRecord::Base时,它意味着Rails有类似的东西

module ActiveRecord
  class Base
  end
end

即一个名为Base的类,它在ActiveRecord模块中,然后被引用为ActiveRecord::Base(你可以在activerecord-n.n.n / lib / active_record / base.rb的Rails源代码中找到它)

::的常见用途是访问模块中定义的常量,例如:

module Math
  PI = 3.141 # ...
end

puts Math::PI

::运算符不允许您绕过标记为private或protected的方法的可见性。


23
投票

如果您可以使用::来暴露任何东西,那么范围(私有,受保护)有什么用?

在Ruby中,一切都暴露出来,一切都可以从其他地方修改。

如果您担心可以从“类定义”之外更改类,那么Ruby可能不适合您。

另一方面,如果您对Java的类被锁定感到沮丧,那么Ruby可能就是您正在寻找的。


11
投票

不,它不是访问每个方法,它是一个“分辨率”运算符,也就是说,您使用它来解析常量/静态符号的范围(或您可以说的位置)。

例如,在你的第一行中,Rails使用它来查找ActiveRecord.Module中的Base类,在第二行中它用于定位Routes类的类方法(静态)等。

它不用于暴露任何东西,它用于“定位”你的范围内的东西。

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


8
投票

添加到以前的答案,使用::访问实例方法是有效的Ruby。以下所有内容均有效:

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

根据最佳实践,我认为只推荐最后一个。


4
投票

这一切都是为了防止定义与链接到项目中的其他代码发生冲突。这意味着你可以把事情分开。

例如,您可以在代码中使用一个名为“run”的方法,您仍然可以调用您的方法,而不是在已链接的其他库中定义的“run”方法。


3
投票

Ruby on rails使用:: for namespace resolution。

class User < ActiveRecord::Base

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

end

要使用它:

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

此外,其他用法是:使用嵌套路由时

OmniauthCallbacksController在用户下定义。

和路线是这样的:

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


class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController

end

3
投票

令人惊讶的是,这里的所有10个答案都说同样的事情。 '::'是命名空间解析运算符,是的,它是真的。但是,当涉及到常量查找算法时,您必须了解名称空间分辨率运算符。正如Matz在他的书“The Ruby Programming Language”中描述的那样,常量查找有多个步骤。首先,它搜索引用常量的词法范围中的常量。如果它没有在词法范围内找到常量,那么它将搜索继承层次结构。由于这种常量查找算法,下面我们得到预期的结果:

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

当F继承自E时,B模块在F的词法范围内。因此,F实例将引用模块B中定义的常量PI。现在,如果模块B没有定义PI,那么F实例将引用PI在超类E中定义的常量

但是,如果我们使用'::'而不是嵌套模块呢?我们会得到相同的结果吗?没有!

通过在定义嵌套模块时使用命名空间解析运算符,嵌套模块和类不再位于其外部模块的词法范围内。如下所示,A :: B中定义的PI不在A :: B :: C :: D的词法范围内,因此在尝试引用get_pi实例方法中的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
© www.soinside.com 2019 - 2024. All rights reserved.