Domanda

Sto pensando a:

class X
    def new()
        @a = 1
    end
    def m( other ) 
         @a == other.@a
    end
end

x = X.new()
y = X.new()
x.m( y ) 

Ma non funziona.

Il messaggio di errore è:

syntax error, unexpected tIVAR

Come faccio a confrontare due attributi privati dalla stessa classe, allora?

È stato utile?

Soluzione

Ci sono diversi metodi

Getter:

class X
  attr_reader :a
  def m( other )
    a == other.a
  end
end

instance_eval:

class X
  def m( other )
    @a == other.instance_eval { @a }
  end
end

instance_variable_get:

class X
  def m( other )
    @a == other.instance_variable_get :@a
  end
end

Non credo che ruby è un concetto di "amico" o "protetti" e anche "privato" è facilmente violati in giro.Utilizzando un getter crea una proprietà di sola lettura, e instance_eval significa che è necessario conoscere il nome della variabile di istanza, quindi, la connotazione è simile.

Altri suggerimenti

Ci sono già state diverse risposte al tuo problema immediato, ma ho notato alcuni altri pezzi di codice che garantisce un commento.(La maggior parte di loro banale, però.)

Ecco quattro banale, tutti quelli connessi con stile di codifica:

  1. Rientro:il missaggio di 4 spazi per il rientro e 5 spazi.In genere è meglio attenersi a solo uno stile di indentazione, e in Rubino, che è generalmente di 2 spazi.
  2. Se un metodo non accetta parametri, è consuetudine lasciare fuori il parantheses nella definizione del metodo.
  3. Allo stesso modo, se si invia un messaggio, senza argomenti, il parantheses sono lasciato.
  4. Senza spazio dopo un'apertura paranthesis e prima della chiusura, tranne per il fatto in blocchi.

Comunque, che solo le piccole cose.Roba grossa è questa:

def new
  @a = 1
end

Questo non non fare quello che tu pensi!Questo definisce un istanza metodo chiamato X#new e non un metodo di una classe chiamata X.new!

Quello che voi chiamate qui:

x = X.new

è un classe metodo chiamato new, che avete ereditato dalla Class classe.Così, non chiamare il tuo nuovo metodo, il che significa che @a = 1 non viene mai eseguito, il che significa che @a è sempre definito, il che significa che sarà sempre valutare di nil il che significa che il @a di self e il @a di other sarà sempre la stessa, il che significa che m sarà sempre true!

Quello che probabilmente si vuole fare è fornire un costruttore, tranne Ruby non sono i costruttori.Ruby utilizza solo metodi factory.

Il metodo davvero volevo sovrascrivere è l' istanza metodo initialize.Ora probabilmente si sta chiedendo:"perché devo eseguire l'override di un istanza metodo chiamato initialize quando sono in realtà la chiamata di una classe metodo chiamato new?"

Beh, costruzione di oggetti in Ruby funziona in questo modo:oggetto di costruzione è divisa in due fasi, allocazione e inizializzazione.L'allocazione è fatto da una classe pubblica, con il metodo allocate, che è definito come un metodo di istanza della classe Class ed è, generalmente, mai ignorate.Solo alloca lo spazio di memoria per l'oggetto e imposta un paio di puntatori, tuttavia, l'oggetto non è realmente utilizzabile a questo punto.

Ecco dove l'inizializzatore è disponibile in:si tratta di un metodo di istanza chiamato initialize, per impostare l'oggetto di stato interne e la porta in un coerente, completamente definita, che può essere utilizzato da altri oggetti.

Così, per creare un nuovo oggetto, quello che dovete fare è questo:

x = X.allocate
x.initialize

[Nota:Objective-C i programmatori possono riconoscere questo.]

Tuttavia, perché è troppo facile dimenticare di chiamare initialize e, come regola generale, un oggetto deve essere pienamente valida dopo la costruzione, c'è una convenienza di fabbrica metodo chiamato Class#new, che fa tutto il lavoro per voi e sembra qualcosa di simile a questo:

class Class
  def new(*args, &block)
    obj = alloc
    obj.initialize(*args, &block)

    return obj
  end
end

[Nota:in realtà, initialize è privato, quindi, la riflessione deve essere utilizzato per aggirare le restrizioni di accesso come questo: obj.send(:initialize, *args, &block)]

Infine, mi permetta di spiegare cosa non va nel tuo m metodo.(Gli altri hanno già spiegato come risolverlo.)

In Ruby, non c'è modo (nota:in Ruby, "non c'è nessun modo" in realtà si traduce in "c'è sempre un modo coinvolgente riflessione") per accedere a una variabile di istanza da fuori l'istanza.Questo è il motivo per cui è chiamato una variabile di istanza, dopo tutto, perché appartiene all'istanza.Questo è un patrimonio da Smalltalk:in Smalltalk non ci sono visibilità restrizioni, tutti i metodi sono pubblici.Pertanto, le variabili di istanza sono il solo modo per fare incapsulamento in Smalltalk, e, dopo tutto, l'incapsulamento è uno dei pilastri di OO.In Ruby, c' sono visibilità restrizioni (come abbiamo visto sopra, per esempio), quindi non è strettamente necessario per nascondere le variabili di istanza per quel motivo.C'è un'altra ragione, comunque:l'Uniforme Principio dell'Accesso.

L'UAP stati che come utilizzare una caratteristica dovrebbe essere indipendente da come la funzione è implementato.Così, l'accesso alla funzione dovrebbe essere sempre la stessa, cioèuniforme.La ragione di questo è che l'autore della funzione è libera di modificare, come la funzione funziona internamente, senza rompere gli utenti della funzionalità.In altre parole, è di base la modularità.

Questo significa ad esempio, che per ottenere le dimensioni di una raccolta dovrebbe essere sempre la stessa, indipendentemente dal fatto che la dimensione è memorizzato in una variabile, calcolata dinamicamente ogni volta che, pigramente calcolata la prima volta e poi memorizzato in una variabile, memoized o qualsiasi altra cosa.Sembra ovvio, ma ad es.Java viene sbagliato:

obj.size # stored in a field

vs

obj.getSize() # computed

Ruby prende la via più facile.In Ruby, c'è solo uno modo d'uso di una funzione:invio di un messaggio.Poiché c'è un solo modo, l'accesso è banalmente uniforme.

Così, per fare una lunga storia breve:semplicemente non è possibile accedere a un'altra istanza della variabile di istanza.è possibile interagire solo con l'istanza tramite l'invio di un messaggio.Il che significa che l'oggetto è di fornire un metodo (in questo caso almeno di protected la visibilità) per accedere sua variabile di istanza, o si devono violare l'oggetto dell'incapsulamento (e quindi perdere un Accesso Uniforme, aumentare l'accoppiamento e il rischio futuro di rottura) utilizzando la reflection (in questo caso instance_variable_get).

Eccolo qui, in tutto il suo splendore:

#!/usr/bin/env ruby

class X
  def initialize(a=1)
    @a = a
  end

  def m(other) 
    @a == other.a
  end

  protected

  attr_reader :a
end

require 'test/unit'
class TestX < Test::Unit::TestCase
  def test_that_m_evaluates_to_true_when_passed_two_empty_xs
    x, y = X.new, X.new
    assert x.m(y)
  end
  def test_that_m_evaluates_to_true_when_passed_two_xs_with_equal_attributes
    assert X.new('foo').m(X.new('foo'))
  end
end

O in alternativa:

class X
  def m(other) 
    @a == other.instance_variable_get(:@a)
  end
end

Quale di questi due hai scelto è una questione di personalee gusto, direi.Il Set classe nella libreria standard, utilizza la riflessione versione, anche se si utilizza instance_eval invece:

class X
  def m(other) 
    @a == other.instance_eval { @a }
  end
end

(Non ho idea del perché.Forse instance_variable_get semplicemente non esisteva quando Set è stato scritto.Ruby sarà di 17 anni, nel mese di febbraio, alcune delle cose nella stdlib è da primi giorni di vita.)

Se non si utilizza il instance_eval opzione (come @jleedev postato), e si sceglie di utilizzare un getter metodo, è possibile mantenere ancora protected

Se si desidera un protected metodo di Ruby, basta effettuare le seguenti operazioni per creare un getter che può essere letto solo da oggetti della stessa classe:

class X
    def new()
        @a = 1
    end
    def m( other ) 
        @a == other.a
    end

    protected
    def a 
      @a
    end
end

x = X.new()
y = X.new()
x.m( y ) # Returns true
x.a      # Throws error

Non sono sicuro, ma questo potrebbe aiutare:

Al di fuori della classe, è un po ' più difficile:

# Doesn't work:
irb -> a.@foo
SyntaxError: compile error
(irb):9: syntax error, unexpected tIVAR
        from (irb):9

# But you can access it this way:
irb -> a.instance_variable_get(:@foo)
    => []

http://whynotwiki.com/Ruby_/_Variables_and_constants#Variable_scope.2Faccessibility

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top