2012-03-16 38 views
6

Qual è la differenza tra:differenza tra "classe A; classe B" e "classe A :: B"

class A 
    class B 
    end 
end 

e

class A 
end 

class A::B 
end 

Aggiornamento: Questi 2 approcci non sono esattamente la stessa.

Nel secondo approccio, B non ha accesso alle costanti definite in A.

Inoltre, come affermato correttamente da Matheus Moreira, nel secondo approccio, è necessario definire A prima che sia possibile definire A::B.

Quali altre differenze ci sono?

risposta

8

In Ruby, moduli e classi sono istanze delle classi Module e Class, rispettivamente. Derivano i loro nomi dalla costante a cui sono assegnati. Quando si scrive:

class A::B 
    # ... 
end 

si sta effettivamente scrivendo:

A::B ||= Class.new do 
    # ... 
end 

Qual è la sintassi assegnazione costante valida, e assume che la costante A è stato inizializzato correttamente e che si riferisce a un Module o Class.

Ad esempio, si consideri come le classi sono solitamente definiti:

class A 
    # ... 
end 

Che cosa sta effettivamente accadendo è questo:

Object::A ||= Class.new do 
    # ... 
end 

Ora, quando si scrive:

class A 
    class B 
    # ... 
    end 
end 

Che in realtà sembra così:

(Object::A ||= Class.new).class_eval do 
    (A::B ||= Class.new).class_eval do 
    # ... 
    end 
end 

Ecco ciò che sta accadendo, in ordine:

  1. Un nuovo Class istanza è asssigned al A costante di Object, a meno che non è stato già inizializzato.
  2. Una nuova istanza Class è assegnata alla costante B di A, a meno che non sia già stata inizializzata.

Questo assicura l'esistenza di tutti classi esterne prima di tentare di definire eventuali classi interne.

C'è anche una modifica dell'ambito, che consente di accedere direttamente alle costanti di A. Confronta:

class A 
    MESSAGE = "I'm here!" 
end 

# Scope of Object 
class A::B 
    # Scope of B 
    puts MESSAGE # NameError: uninitialized constant A::B::MESSAGE 
end 

# Scope of Object 
class A 
    # Scope of A 
    class B 
    # Scope of B 
    puts MESSAGE # I'm here! 
    end 
end 

Secondo this blog post, il core team di Ruby chiama la "classe corrente", il cref. Sfortunatamente, l'autore non elabora, ma come nota, è separato dal contesto di self.


As explained here, il cref è un elenco collegata che rappresenta l'annidamento di moduli a un certo punto nel tempo.

La corrente cref viene utilizzato per costante e variabile di classe ricerca e per def, undef e alias.


Come gli altri hanno detto, sono diversi modi di esprimere la stessa cosa.

C'è, tuttavia, una sottile differenza. Quando si scrive class A::B, si si assume che la classe A è già stata definita. In caso contrario, riceverai uno NameError e lo B non sarà definito affatto.

scrivere moduli correttamente annidati:

class A 
    class B 
    end 
end 

Assicura la classe A esiste prima di tentare di definire B.

+1

Ho aggiornato la domanda per dimostrare che questi due approcci non sono gli stessi. – nickh

+0

@nickh, non è necessario modificare la domanda per includere le risposte. Dovresti [aggiornare le risposte che sono state utili e accettare quella che ha sicuramente risposto alla tua domanda] (http://stackoverflow.com/faq#howtoask). –

+0

Il fatto è che nessuno di loro ha risposto alla domanda. Tutte le risposte dicono che i 2 approcci sono gli stessi, che non sono. – nickh

1

Sono uguali. Sono diversi modi di scrivere la stessa cosa. Il primo è il modo ingenuo di scriverlo, ma spesso diventa difficile tenere traccia del nesting una volta che la classe/modulo diventa grande. Usando il secondo modo, puoi evitare di annidare nell'aspetto.

+1

Ho aggiornato la domanda per mostrare che questi due approcci non sono gli stessi. – nickh

+0

@nickh Ho pensato che queste differenze fossero banali e presupposte, quindi non le ho menzionate. Indicandoli non è molto diverso dal dire "beh, la stringa' classe B' sembra diversa da 'classe A :: B'", ma non l'hai menzionata. – sawa

3

Due modi diversi di dire la stessa cosa. Quella cosa è che la classe B è una classe interna o nidificata e può essere raggiunta solo attraverso l'interfaccia A.

> class A 
.. def say 
.... "In A" 
....end 
.. 
.. class B 
.... def say 
...... "In B" 
......end 
....end 
..end 
=> nil 

> A.new.say 
=> "In A" 
> B.new.say 
=> #<NameError: uninitialized constant B> 
> A::B.new.s­ay 
=> "In B" 

contro

> class A 
.. def say 
.... "In A" 
....end 
..end 
=> nil 

> class A::B 
.. def say 
.... "In B" 
....end 
..end 
=> nil 

> A.new.say 
=> "In A" 
> B.new.say 
=> #<NameError: uninitialized constant B> 
> A::B.new.s­ay 
=> "In B" 
> 
+1

Ho aggiornato la domanda per dimostrare che questi due approcci non sono gli stessi. – nickh

Problemi correlati