Rubyでのクラス、インスタンス、メタクラスの解明
Ruby Magicの新しいエピソードへようこそ!今月のエディションはすべてメタクラスに関するもので、2人の開発者(Hi Maud!)の間の議論によって引き起こされた主題です。
メタクラスを調べることで、Rubyでクラスメソッドとインスタンスメソッドがどのように機能するかを学びます。途中で、明示的な「definee」を渡すことによってメソッドを定義することと、class << self
を使用することの違いを発見してください。 またはinstance_eval
。行きましょう!
クラスインスタンスとインスタンスメソッド
Rubyでメタクラスが使用される理由を理解するために、インスタンスメソッドとクラスメソッドの違いを調べることから始めます。
Rubyでは、クラス 他のオブジェクトを作成するための青写真を定義するオブジェクトです。クラスは、そのクラスの任意のインスタンスで使用できるメソッドを定義します。
クラス内でメソッドを定義すると、インスタンスメソッドが作成されます そのクラスで。そのクラスの将来のインスタンスでは、そのメソッドを使用できるようになります。
class User
def initialize(name)
@name = name
end
def name
@name
end
end
user = User.new('Thijs')
user.name # => "Thijs"
この例では、User
という名前のクラスを作成します 、インスタンスメソッド 名前付き#name
ユーザーの名前を返します。次に、クラスを使用して、クラスインスタンスを作成します。 user
という名前の変数に保存します 。 user
以降 User
のインスタンスです クラスには、#name
があります 利用可能な方法。
クラスは、インスタンスメソッドをメソッドテーブルに格納します 。そのクラスのインスタンスはすべて、そのクラスのメソッドテーブルを参照して、そのインスタンスメソッドにアクセスします。
クラスオブジェクト
クラスメソッド 最初にインスタンスを作成しなくても、クラスで直接呼び出すことができるメソッドです。クラスメソッドは、名前の前にself.
を付けることで作成されます。 それを定義するとき。
クラス自体はオブジェクトです。定数はクラスオブジェクトを参照するため、定数に定義されているクラスメソッドは、アプリケーションのどこからでも呼び出すことができます。
class User
# ...
def self.all
[new("Thijs"), new("Robert"), new("Tom")]
end
end
User.all # => [#<User:0x00007fb01701efb8 @name="Thijs">, #<User:0x00007fb01701ef68 @name="Robert">, #<User:0x00007fb01701ef18 @name="Tom">]
self.
で定義されたメソッド -プレフィックスはクラスのメソッドテーブルに追加されません。代わりに、クラスのメタクラスに追加されます。
メタクラス
クラスを除いて、Rubyの各オブジェクトには隠されたメタクラスがあります。メタクラスはシングルトンであり、単一のオブジェクトに属していることを意味します。クラスの複数のインスタンスを作成する場合、それらは同じクラスを共有しますが、それらはすべて別々のメタクラスを持ちます。
thijs, robert, tom = User.all
thijs.class # => User
robert.class # => User
tom.class # => User
thijs.singleton_class # => #<Class:#<User:0x00007fb71a9a2cb0>>
robert.singleton_class # => #<Class:#<User:0x00007fb71a9a2c60>>
tom.singleton_class # => #<Class:#<User:0x00007fb71a9a2c10>>
この例では、各オブジェクトのクラスがUser
であることがわかります。 、それらのシングルトンクラスは異なるオブジェクトIDを持っています。つまり、それらは別々のオブジェクトです。
Rubyはメタクラスにアクセスできるため、既存のオブジェクトにメソッドを直接追加できます。そうしても、オブジェクトのクラスに新しいメソッドは追加されません。
robert = User.new("Robert")
def robert.last_name
"Beekman"
end
robert.last_name # => "Beekman"
User.new("Tom").last_name # => NoMethodError (undefined method `last_name' for #<User:0x00007fe1cb116408>)
この例では、#last_name
を追加します robert
に保存されているユーザーに 変数。 robert
User
のインスタンスです 、新しく作成されたUser
のインスタンス #last_name
にアクセスできなくなります robert
にのみ存在するメソッド のメタクラス。
self
とは ?
メソッドを定義してレシーバーを渡すと、新しいメソッドは、クラスのメソッドテーブルに追加されるのではなく、レシーバーのメタクラスに追加されます。
tom = User.new("Tom")
def tom.last_name
"de Bruijn"
end
上記の例では、#last_name
を追加しました tom
に直接 オブジェクト、tom
を渡すことによって メソッドを定義するときの受信者として。
これは、クラスメソッドでも機能します。
class User
# ...
def self.all
[new("Thijs"), new("Robert"), new("Tom")]
end
end
ここでは、self
を明示的に渡します .all
を作成するときにレシーバーとして 方法。クラス定義では、self
クラスを参照します(User
この場合)、.all
メソッドがUser
に追加されます のメタクラス。
User
は定数に格納されているオブジェクトであるため、参照するたびに同じオブジェクトと同じメタクラスにアクセスします。
メタクラスを開く
クラスメソッドは、クラスオブジェクトのメタクラスのメソッドであることを学びました。これを知って、以前に見たことがあるかもしれないクラスメソッドを作成する他のいくつかのテクニックを見ていきます。
class << self
少し時代遅れになっていますが、一部のライブラリはclass << self
を使用しています クラスメソッドを定義します。この構文トリックは、現在のクラスのメタクラスを開き、それと直接対話します。
class User
class << self
self # => #<Class:User>
def all
[new("Thijs"), new("Robert"), new("Tom")]
end
end
end
User.all # => [#<User:0x00007fb01701efb8 @name="Thijs">, #<User:0x00007fb01701ef68 @name="Robert">, #<User:0x00007fb01701ef18 @name="Tom">]
この例では、User.all
という名前のクラスメソッドを作成します User
にメソッドを追加する のメタクラス。前に見たようにメソッドのレシーバーを明示的に渡す代わりに、self
を設定します User
へ User
の代わりにのメタクラス
前に学習したように、明示的なレシーバーのないメソッド定義は、現在のクラスのインスタンスメソッドとして追加されます。ブロック内では、現在のクラスはUser
です。 のメタクラス(#<Class:User>
。
instance_eval
別のオプションは、instance_eval
を使用することです 、これは同じことを行いますが、大きな違いが1つあります。クラスのメタクラスはブロックで定義されたメソッドを受け取りますが、self
メインクラスへの参照のままです。
class User
instance_eval do
self # => User
def all
[new("Thijs"), new("Robert"), new("Tom")]
end
end
end
User.all # => [#<User:0x00007fb01701efb8 @name="Thijs">, #<User:0x00007fb01701ef68 @name="Robert">, #<User:0x00007fb01701ef18 @name="Tom">]
この例では、User
にインスタンスメソッドを定義します。 のメタクラスは以前と同じですが、self
引き続きUser
を指します 。通常は同じオブジェクトを指しますが、「デフォルトの定義」とself
さまざまなオブジェクトを指すことができます。
私たちが学んだこと
クラスはメソッドを持つことができる唯一のオブジェクトであり、インスタンスメソッドは実際にはオブジェクトのメタクラスのメソッドであることを学びました。 class << self
self
を交換するだけです メタクラスでメソッドを定義できるようにするために、instance_eval
ほとんど同じことをします(ただし、self
には触れません 。
メタクラスを明示的に操作することはありませんが、Rubyは内部でメタクラスを広範囲に使用します。メソッドを定義するときに何が起こるかを知ることは、Rubyがそのように動作する理由(およびクラスメソッドの前にself.
を付ける必要がある理由を理解するのに役立ちます。 。
読んでくれてありがとう。読んだ内容が気に入った場合は、Ruby Magicに登録して、月に1回程度の新しい記事が公開されたときに電子メールを受信することをお勧めします。
-
Rubyのカスタム例外
Rubyで独自の例外を作成するのは簡単です。次の手順に従ってください: 1。新しいクラスを作成する 例外は、Rubyの他のすべてと同じように、クラスです。新しい種類の例外を作成するには、StandardErrorまたはその子の1つから継承するクラスを作成するだけです。 class MyError < StandardError end raise MyError 慣例により、新しい例外のクラス名は「エラー」で終わります。カスタム例外をモジュール内に配置することもお勧めします。つまり、最終的なエラークラスは次のようになります:ActiveRecord::RecordNotFound
-
なぜクラスを作成するのですか?
前回の記事に続いて、なぜnilを使用するのか、私たちが当たり前と思っている他のことについてこの質問をするのは良い考えだと思いました。 いいね… 醜いコードの巨大なブロブを1つだけ持つのではなく、なぜクラスを使用するのですか? 基本的なレベルでは、クラスを使用してコードとデータを論理ユニットに編成します。 しかし、それだけではありません。 クラスを使用すると、抽象化を作成できます 。 抽象化とは何ですか? 毎日、1分ごとに抽象化を使用します。 キーボード 私はこれを入力しています: ケーブル チェリーMXブラックスイッチ すべてをまとめるプラスチックケース 電気信号をコンピュータ