【问题标题】:What is Ruby's double-colon `::`?什么是 Ruby 的双冒号`::`?
【发布时间】:2011-03-01 20:52:21
【问题描述】:

这个双冒号::是什么?例如。 Foo::Bar.

我找到了definition

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

如果您可以使用:: 公开任何内容,那么范围(私有、受保护)有什么用?

【问题讨论】:

标签: ruby syntax operators


【解决方案1】:

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

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

您可以从模块外部以SomeModule::InnerModule::MyClass::CONSTANT 的身份访问CONSTANT

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

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

【讨论】:

  • 在 C# 中,例如,是的。另一方面,C++(和 Ruby)使用 :: 进行命名空间解析,例如 std::cout << "Hello World!";
  • 相关说明:如果要回到顶级命名空间,请这样做:::SomeModule
  • @Benjamin 前导冒号是隐含的,除非我碰巧在另一个模块中有一个 SomeModule 并且我想获得顶级的,对吗?
  • @Jo 是的。如果您想确保引用的是顶级命名空间中的常量或另一个模块中具有相同名称的常量(例如 ::SomeOtherModule::ClassMethods),这将很有帮助。
  • 这很像 C++ 的作用域操作数
【解决方案2】:

这个简单的例子说明了这一点:

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

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

【讨论】:

  • 这就是导致警告的原因。有没有办法逃避警告?
  • @NullVoxPopuli 通常修改常量是一件非常糟糕的事情,但如果你想修改一个写得不好的 gem 中的常量并且不想分叉它,可以使用 .send(:remove_const) 来完成到定义它的模块,然后重新定义常量。
【解决方案3】:

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

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

module ActiveRecord
  class Base
  end
end

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

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

module Math
  PI = 3.141 # ...
end

puts Math::PI

:: 运算符不允许您绕过标记为私有或受保护的方法的可见性。

【讨论】:

  • 如果有class MyClass < ActiveRecord::Base,这是否意味着MyClass 只从类库继承方法,而不是ActiveRecord 模块内的任何东西?
  • 为什么在这个命名空间解析中使用特殊的双冒号而不是使用“.”也是为了这个?即使我们使用“.”,上下文和大写也可以防止含义混淆,不是吗?
  • @Jonah 在某些情况下会模棱两可。例如考虑class Foo; Baz = 42; def self.Baz; "Baz method!"; end; end(完全有效)Foo::Baz # => 42Foo.Baz # => "Baz method!"。请注意,Foo::Baz()(带括号)也会调用该方法。
  • 那么它解决的用例是具有类常量和具有完全相同名称的类方法的能力?这似乎不是支持该功能的有力论据。就个人而言,我宁愿失去这种能力(无论如何似乎很麻烦),失去双冒号,并使用“。”也用于命名空间......也许它解决了其他用例?
【解决方案4】:

如果您可以使用 :: 来公开任何内容,那么范围(私有的、受保护的)有什么好处?

在 Ruby 中,一切都是公开的,一切都可以从其他任何地方进行修改。

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

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

【讨论】:

  • 我听说一些红宝石学家说实例变量没有公开,甚至attr_accessor 也只是创建了修改变量的方法。 (然后又是instance_eval
  • 正确,有instance_eval。但也有instance_variable_getinstance_variable_set。 Ruby 对于约束来说太动态了。
【解决方案5】:

除了之前的答案,使用:: 访问实例方法是有效的Ruby。以下都是有效的:

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

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

【讨论】:

    【解决方案6】:

    令人惊讶的是,这里的所有 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
    

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

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

    通过在定义嵌套模块时使用命名空间解析运算符,嵌套模块和类不再位于其外部模块的词法范围内。正如您在下面看到的,在 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
    

    【讨论】:

    • 答案的顺序应该更高。
    【解决方案7】:

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

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

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

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

    【讨论】:

    • “(静态)”是指“(画)”吗?!?
    【解决方案8】:

    Ruby on rails 使用:: 进行命名空间解析。

    class User < ActiveRecord::Base
    
      VIDEOS_COUNT = 10
      Languages = { "English" => "en", "Spanish" => "es", "Mandarin Chinese" => "cn"}
    
    end
    

    使用它:

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

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

    OmniauthCallbacksController 在 users 下定义。

    并路由为:

    devise_for :users, controllers: {omniauth_callbacks: "users/omniauth_callbacks"}
    
    
    class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
    
    end
    

    【讨论】:

      【解决方案9】:

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

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

      【讨论】:

        【解决方案10】:
        module Amimal
              module Herbivorous
                    EATER="plants" 
              end
        end
        
        Amimal::Herbivorous::EATER => "plants"
        

        :: 用于创建范围。为了从 2 个模块访问常量 EATER,我们需要对模块进行范围以达到常量

        【讨论】:

          【解决方案11】:

          简单来说,它是一个命名空间, 现在命名空间是模块、类、函数和其他的容器。它还有助于解决问题名称冲突。 在 ruby​​ 中,您可以按模块访问命名空间,例如

          module A
            class Article
               def Base
               end
            module B
            end
          end
          

          所以要访问我们使用 A::Article 类的文章。 在某些情况下,您会看到 A::Article<:base>

          【讨论】:

            猜你喜欢
            • 2011-01-17
            • 2012-12-20
            • 2019-09-14
            • 2016-05-16
            • 1970-01-01
            • 2013-05-18
            • 1970-01-01
            • 2011-08-21
            相关资源
            最近更新 更多