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

Mel*_*emi 410 ruby syntax operators

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

我找到了一个定义:

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

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

mip*_*adi 362

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

module SomeModule
    module InnerModule
        class MyClass
            CONSTANT = 4
        end
    end
end
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • 相关说明:如果要返回顶级命名空间,请执行以下操作:`:: SomeModule` (136认同)
  • @Jo是的.如果要确保在顶级命名空间中引用常量或在另一个模块中引用具有相同名称的常量(例如:: SomeOtherModule :: ClassMethods),则会很有帮助. (7认同)
  • 例如,在C#中,是的.另一方面,C++(和Ruby)使用`::`进行名称空间解析,例如`std :: cout <<"Hello World!";` (5认同)
  • @Benjamin领先的冒号是暗示的,除非我碰巧在另一个模块中有一个SomeModule而我想要获得顶级冒号,对吗? (5认同)
  • 这非常类似于C++的范围操作数 (2认同)

Nad*_*der 107

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

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
Run Code Online (Sandbox Code Playgroud)

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

  • @NullVoxPopuli通常修改常量是一件非常糟糕的事情但是如果你想要在一个编写错误的gem中修改一个常量并且不想分叉它,可以通过使用[.send(:remove_const)]来完成(http: //apidock.com/ruby/Module/remove_const)到定义它的模块,然后重新定义常量. (3认同)

mik*_*kej 70

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

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

module ActiveRecord
  class Base
  end
end
Run Code Online (Sandbox Code Playgroud)

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

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

module Math
  PI = 3.141 # ...
end

puts Math::PI
Run Code Online (Sandbox Code Playgroud)

::运营商不允许你绕过的方法可视性标记为私有或保护.

  • 因此,如果有一个类MyClass <ActiveRecord :: Base`,那是否意味着MyClass只继承类基类中的方法而不是ActiveRecord模块中的任何内容? (7认同)
  • @Jonah在某些情况下它会模棱两可.例如,考虑`类Foo; Baz = 42; def self.Baz; "Baz方法!"; 结束; 结束`(完全有效)`Foo :: Baz#=> 42`和`Foo.Baz#=>"Baz方法!"`.请注意,`Foo :: Baz()`(带括号)也会调用该方法. (3认同)
  • 因此,用例解决了具有类常量和具有完全相同名称的类方法的能力吗?似乎不太赞成该功能。就我个人而言,我宁愿失去这种能力(无论如何似乎还是麻烦),失去双冒号并使用“”。也可以用于命名空间...。也许还有其他用例可以解决? (3认同)
  • 为什么使用特殊的双冒号来进行此命名空间解析而不是使用".".对于这个呢?即使我们使用".",上下文和大写也可以防止意义混淆,不是吗? (2认同)

yfe*_*lum 24

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

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

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

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

  • 正确,有`instance_eval`.但是还有`instance_variable_get`和`instance_variable_set`.Ruby对于约束来说太动态了. (4认同)

Fra*_*oto 11

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

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

它不用于暴露任何东西,它用于"定位"你的范围内的东西.

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


Yur*_*sev 10

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

MyClass::new::instance_method
MyClass::new.instance_method
MyClass.new::instance_method
MyClass.new.instance_method
Run Code Online (Sandbox Code Playgroud)

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


小智 6

Ruby on Rails 用于::名称空间解析。

class User < ActiveRecord::Base

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

end
Run Code Online (Sandbox Code Playgroud)

使用它:

User::VIDEOS_COUNT
User::Languages
User::Languages.values_at("Spanish") => "en"
Run Code Online (Sandbox Code Playgroud)

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

OmniauthCallbacksController定义在用户下。

并路由为:

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


class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController

end
Run Code Online (Sandbox Code Playgroud)


Mon*_*ong 5

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

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


Don*_*ato 5

出乎意料的是,这里的所有10个答案都说同一件事。'::'是名称空间解析运算符,是的。但是,对于常量查找算法,您必须了解名称空间解析运算符。正如Matz在他的《 Ruby编程语言》一书中所描述的那样,持续查找有多个步骤。首先,它在引用该常量的词法范围内搜索常量。如果未在词法范围内找到常量,则将搜索继承层次结构。由于采用了这种恒定查找算法,因此我们在下面得到了预期的结果:

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
Run Code Online (Sandbox Code Playgroud)

尽管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
Run Code Online (Sandbox Code Playgroud)

  • 这应该按答案的顺序排列较高。 (4认同)