インスタンス変数:自己vs @


179

ここにいくつかのコードがあります:

class Person
  def initialize(age)
    @age = age
  end

  def age
    @age
  end

  def age_difference_with(other_person)
    (self.age - other_person.age).abs
  end

  protected :age
end

私が知りたいのは、使用の違い@ageself.ageage_difference_with方法。

回答:


260

書き込み@ageはインスタンス変数に直接アクセスします@age。書き込みself.ageは、オブジェクトにそれ自体にメッセージを送信するように指示しますage。これは通常、インスタンス変数を返します@ageが、ageメソッドが特定のサブクラスでどのように実装されているかに応じて、他の多くのことを実行できます。たとえば、実際の年齢よりも10歳若い年齢を常に報告するMiddleAgedSocialiteクラスがあるとします。または、より実際的には、PersistentPersonクラスが永続ストアからそのデータを遅延読み込みし、すべての永続データをハッシュにキャッシュする場合があります。


2
私はかつてRailsで本を読んだことがあり、このselfと@の違いを理解していないので、パブリックインターフェイスを使用してデータを作成するには、常にメソッド(セッターとゲッターではない)でself.var_nameを使用する必要があります。ゲッターとセッターでそれを定義するのに時間を費やしましたよね?
sarunw

1
...英語...いくつもの意味は?私は与えられた最後の2つの例を取得しませんでした。
user2167582 2014年

23

違いは、メソッドの使用とその実装を分離していることです。プロパティの実装を変更する場合(たとえば、誕生日を保持し、現在と誕生日の時間差に基づいて年齢を計算するなど)は、メソッドに依存するコードを変更する必要はありません。プロパティを直接使用する場合、変更はコードの他の領域に伝播する必要があります。この意味で、プロパティを直接使用する方が、クラスが提供するインターフェースを使用するよりも脆弱です。


15
ああ、self.ageはインスタンス変数またはインスタンスメソッドのいずれかを参照できるためですか?
Nolan Amy

@。@ ...これは悲しいことです
cyc115 2018年

7

初期化子Struct.newを生成するための適切な方法であるクラスを継承するときに警告されます(Rubyで初期化子を生成する方法は?

class Node < Struct.new(:value)
    def initialize(value)
        @value = value
    end
    def show()
        p @value
        p self.value # or `p value`
    end
end 

n = Node.new(30)
n.show()

戻ります

30
nil

ただし、イニシャライザを削除すると、イニシャライザが返されます

nil
30

クラス定義あり

class Node2
    attr_accessor :value
    def initialize(value)
        @value = value
    end
    def show()
        p @value
        p self.value
    end
end

コンストラクタを提供する必要があります。

n2 = Node2.new(30)
n2.show()

戻ります

30
30

@Prosseekの例をありがとう、私は現在Ruby on Railsを学んでおり、これはまさにRubyが不必要に複雑に感じられるような種類の振る舞いです。
cyc115 2018年

3

最初の答えは完全に正しいですが、比較的初心者なので、それが何を意味するのかすぐにはわかりませんでした(自分にメッセージを送信するのですか?ええと...)。短い例が役立つと思います:

class CrazyAccessors
  def bar=(val)
    @bar = val - 20 # sets @bar to (input - 20)
  end
  def bar
    @bar
  end

  def baz=(value)
    self.bar = value # goes through `bar=` method, so @bar = (50 - 20)
  end

  def quux=(value)
    @bar = value     # sets @bar directly to 50
  end
end

obj  = CrazyAccessors.new
obj.baz = 50
obj.bar  # => 30
obj.quux = 50
obj.bar  # => 50

8
この例は、物事をより混乱させました。
Oskar Holmkratz 2016

1
申し訳ありませんが、例のコメントは十分ではありません。私はあなたの推論をたどることができません。
kouty 16

Smalltalkから来た人は、オブジェクトは「メッセージをそれ自体に送信する」と言います。Pythonから来た人は、オブジェクトは「それ自体でメソッドを呼び出す」と言います。混乱しないでください。それらはまったく同じものです。(セマンティクスの純粋主義者は、動的型付けの言語でのみ同じであり、C ++仮想メソッド呼び出しはメッセージの送信とまったく同じではないことに反対するかもしれません。純粋主義者は正しいですが、おそらくこの質問の範囲を超えています/回答)
GrandOpener

私は例が好きですが、実際に何が起こっているかについていくつかコメントを追加してください。説明なしで従うのは難しい
CalamityAdam

2

違いはありません。お互いを見てself.ageother_person.age近くで見るというドキュメンタリーの価値のためだけに行われたのではないでしょうか。

将来的に実際のゲッターを使用できるようになると思います。インスタンス変数を返すだけではなく、もっと複雑なことを行う可能性があります。その場合、メソッドを変更する必要はありません。

しかし、これは、オブジェクトの実装が変更された場合に他のメソッドを変更することが合理的であると考えると、ありそうもない抽象化です。ある時点で、オブジェクト自体内の単純な参照が完全に合理的です。

いずれの場合ageでもself、プレーンageのアクセサも呼び出されたため、プロパティの抽象化はの明示的な使用を説明していません。


-3

@age-間違いなくインスタンス変数の年齢

self.age-インスタンスプロパティの年齢を指します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.