Ce este Ruby’s double-colon `::`? (Programare, Ruby, Sintaxă, Operatori)

Meltemi a intrebat.

Ce este acest dublu-colon ::? De ex. Foo::Bar.

Am găsit un definiție:

The :: este un operator unar care permite ca: constantele, metodele de instanță și metodele de clasă definite în cadrul unei clase sau al unui modul să fie accesate de oriunde din afara clasei sau a modulului.

La ce bun domeniul de aplicare (private, protected) dacă poți folosi pur și simplu :: pentru a expune orice?

Comentarii

    185

  • În beneficiul viitorilor gogleri, dacă încercați să căutați un simbol, încercați symbolhound.com –  > Por Andrew Grimm.
  • Posibil duplicat? stackoverflow.com/questions/2276905/what-does-mean-in-ruby –  > Por jacobq.
  • precum și stackoverflow.com/questions/3597096/… –  > Por 1dolinski.
  • Fii binecuvântat, @AndrewGrimm. Acesta este cel mai bun lucru pe care l-am văzut săptămâna aceasta. –  > Por abeger.
10 răspunsuri
mipadi

:: este practic un operator de rezolvare a spațiului de nume. Vă permite să accesați elemente din module sau elemente la nivel de clasă în clase. De exemplu, să zicem că aveți această configurație:

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

Ați putea accesa CONSTANT din afara modulului ca SomeModule::InnerModule::MyClass::CONSTANT.

Aceasta nu afectează metodele de instanță definite într-o clasă, deoarece le accesați cu o sintaxă diferită (punctul .).

Notă relevantă: Dacă doriți să reveniți la spațiul de nume de nivel superior, procedați astfel: ::SomeModule – Benjamin Oakes

Comentarii

  • În C#, de exemplu, da. Pe de altă parte, C++ (și Ruby) utilizează :: pentru rezolvarea spațiului de nume, cum ar fi std::cout << "Hello World!"; –  > Por Jerry Fernholz.
  • 148

  • Notă relevantă: Dacă doriți să vă întoarceți la spațiul de nume de nivel superior, procedați astfel: ::SomeModule –  > Por Benjamin Oakes.
  • @Benjamin Colonele de început sunt implicite, cu excepția cazului în care se întâmplă să am un SomeModule în interiorul unui alt modul și vreau să obțin în schimb cel de nivel superior, corect? –  > Por Jo Liss.
  • @Jo Da. Poate fi util dacă doriți să vă asigurați că vă referiți la o constantă din spațiul de nume de nivel superior sau la o constantă cu același nume dintr-un alt modul (de exemplu ::SomeOtherModule::ClassMethods). –  > Por Benjamin Oakes.
  • Acest lucru seamănă foarte mult cu scope operand din C++ –  > Por lkahtz.
Nader

Acest exemplu simplu ilustrează acest lucru:

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

Preluat din http://www.tutorialspoint.com/ruby/ruby_operators.htm

Comentarii

  • aceasta este ceea ce provoacă totuși avertismentul. Există o modalitate de a evita avertismentul? –  > Por NullVoxPopuli.
  • @NullVoxPopuli În general, modificarea constantelor este un lucru foarte rău, dar dacă, de exemplu, doriți să modificați o constantă într-o bijuterie prost scrisă și nu doriți să o utilizați ca fork, puteți face acest lucru folosind .send(:remove_const) către modulul care o definește, apoi redefinind constanta. –  > Por BookOfGreg.
mikej

:: Vă permite să accesați o constantă, un modul sau o clasă definită în interiorul unei alte clase sau module. Este utilizat pentru a oferi spații de nume astfel încât numele metodelor și ale claselor să nu intre în conflict cu alte clase de autori diferiți.

Când vedeți ActiveRecord::Base în Rails înseamnă că Rails are ceva de genul

module ActiveRecord
  class Base
  end
end

adică o clasă numită Base în interiorul unui modul ActiveRecord care este apoi referită ca ActiveRecord::Base (puteți găsi acest lucru în sursa Rails în activerecord-n.n.n/lib/active_record/base.rb)

O utilizare obișnuită a lui :: este de a accesa constantele definite în module, de ex.

module Math
  PI = 3.141 # ...
end

puts Math::PI

The :: operator nu vă permite să ocoliți vizibilitatea metodelor marcate ca fiind private sau protejate.

Comentarii

  • Așadar, dacă se are class MyClass < ActiveRecord::Base, înseamnă că MyClass moștenește doar metodele din clasa de bază și nu orice lucru din interiorul modulului ActiveRecord? –  > Por Charlie Parker.
  • De ce să folosiți punctul dublu special pentru această rezoluție a spațiului de nume, în loc să folosiți „.” și pentru aceasta? Contextul și majusculele ar preveni confuzia de sens chiar dacă am folosi „.”, nu-i așa? –  > Por Jonah.
  • @Jonah există unele cazuri în care ar fi ambiguu. de exemplu, luați în considerare class Foo; Baz = 42; def self.Baz; "Baz method!"; end; end (perfect valabil) Foo::Baz # => 42 și Foo.Baz # => "Baz method!". Rețineți că Foo::Baz() (cu paranteze) ar apela, de asemenea, metoda. –  > Por mikej.
  • Deci, cazul de utilizare pe care îl rezolvă este capacitatea de a avea o constantă de clasă și o metodă de clasă care au exact același nume? Acesta nu pare a fi un argument puternic în favoarea acestei caracteristici. Personal, aș prefera mai degrabă să pierd această abilitate (oricum, pare a fi o problemă), să pierd dublul punct și să folosesc „.” pentru spațierea numelor…. Poate că există cazuri de utilizare suplimentare pe care le rezolvă? –  > Por Jonah.
yfeldblum

La ce bun domeniul de aplicare (privat, protejat) dacă poți folosi :: pentru a expune orice?

În Ruby, totul este expus și totul poate fi modificat de oriunde altundeva.

Dacă vă îngrijorează faptul că clasele pot fi modificate din afara „definiției clasei”, atunci probabil că Ruby nu este pentru dumneavoastră.

Pe de altă parte, dacă sunteți frustrat de faptul că clasele din Java sunt blocate, atunci Ruby este probabil ceea ce căutați.

Comentarii

  • I-am auzit pe unii rubiști spunând că variabilele de instanță nu sunt expuse, că nici măcar attr_accessor doar face metode care modifică variabila. (Apoi, din nou, există instance_eval) –  > Por Andrew Grimm.
  • Corect, există instance_eval. Dar există și instance_variable_get și instance_variable_set. Ruby este pur și simplu prea dinamic pentru constrângeri. –  > Por yfeldblum.
Yuri Ghensev

Adăugând la răspunsurile anterioare, este valabil ca Ruby să folosească :: pentru a accesa metodele de instanță. Toate următoarele sunt valabile:

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

Conform celor mai bune practici, cred că doar ultima este recomandată.

Donato

Surprinzător, toate cele 10 răspunsuri de aici spun același lucru. ‘:::’ este un operator de rezolvare a spațiului de nume și da, este adevărat. Dar există o problemă de care trebuie să vă dați seama cu privire la operatorul de rezolvare a spațiului de nume atunci când vine vorba de algoritmul de căutare a constantelor. După cum descrie Matz în cartea sa, „The Ruby Programming Language”, căutarea constantelor are mai multe etape. În primul rând, se caută o constantă în domeniul lexical în care se face referire la constantă. Dacă nu găsește constanta în cadrul domeniului lexical, atunci caută în ierarhia de moștenire. Datorită acestui algoritm de căutare a constantelor, mai jos vom obține rezultatele așteptate:

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

În timp ce F moșteneș te din E, modulul B se află în domeniul lexical al lui F. În consecință, instanțele lui F se vor referi la constanta PI definită în modulul B. Dacă modulul B nu a definit PI, atunci instanțele lui F se vor referi la constanta PI definită în superclasa E.

Dar dacă am folosi „:::” în loc de modulele de anvelopare? Am obține același rezultat? Nu!

Prin utilizarea operatorului de rezoluție a spațiului de nume la definirea modulelor imbricate, modulele și clasele imbricate nu se mai află în domeniul lexical al modulelor exterioare. După cum puteți vedea mai jos, PI definit în A::B nu se află în domeniul lexical al A::B::C::D și, prin urmare, obținem o constantă neinițializată atunci când încercăm să ne referim la PI în metoda de instanță 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

Francisco Soto

Nu, nu este pentru a accesa fiecare metodă, este un operator de „rezoluție”, adică îl utilizați pentru a rezolva domeniul de aplicare (sau locația, se poate spune) a unui simbol constant/static.

De exemplu, în prima dintre liniile tale, Rails îl folosește pentru a găsi clasa Base în interiorul ActiveRecord.Module, în a doua este folosit pentru a localiza metoda de clasă (statică) a clasei Routes, etc., etc.

Nu este folosit pentru a expune nimic, ci pentru a „localiza” lucruri în jurul domeniului de aplicare.

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

Comentarii

  • prin „(static)” vrei să spui „(draw)”?!? –  > Por Meltemi.
Mongus Pong

Este vorba de a preveni ca definițiile să se ciocnească cu alte coduri legate de proiectul dumneavoastră. Înseamnă că puteți păstra lucrurile separate.

De exemplu, puteți avea o metodă numită „run” în codul dvs. și veți putea să apelați metoda dvs. și nu metoda „run” care a fost definită într-o altă bibliotecă pe care ați conectat-o.

Francesca Rodricks
module Amimal
      module Herbivorous
            EATER="plants" 
      end
end

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

:: Este utilizat pentru a crea un domeniu de aplicare . Pentru a accesa constanta EATER din 2 module, trebuie să extindem domeniul de aplicare al modulelor pentru a ajunge până la constantă

Pankhuri

Ruby on rails folosește :: pentru rezolvarea spațiului de nume.

class User < ActiveRecord::Base

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

end

Pentru a o utiliza :

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

De asemenea, o altă utilizare este : Atunci când se utilizează rute imbricate

OmniauthCallbacksController este definită în cadrul utilizatorilor.

Și rutate ca:

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


class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController

end