Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

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を設定します UserUserの代わりにのメタクラス

前に学習したように、明示的なレシーバーのないメソッド定義は、現在のクラスのインスタンスメソッドとして追加されます。ブロック内では、現在のクラスは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回程度の新しい記事が公開されたときに電子メールを受信することをお勧めします。


  1. Rubyのカスタム例外

    Rubyで独自の例外を作成するのは簡単です。次の手順に従ってください: 1。新しいクラスを作成する 例外は、Rubyの他のすべてと同じように、クラスです。新しい種類の例外を作成するには、StandardErrorまたはその子の1つから継承するクラスを作成するだけです。 class MyError < StandardError end raise MyError 慣例により、新しい例外のクラス名は「エラー」で終わります。カスタム例外をモジュール内に配置することもお勧めします。つまり、最終的なエラークラスは次のようになります:ActiveRecord::RecordNotFound

  2. なぜクラスを作成するのですか?

    前回の記事に続いて、なぜnilを使用するのか、私たちが当たり前と思っている他のことについてこの質問をするのは良い考えだと思いました。 いいね… 醜いコードの巨大なブロブを1つだけ持つのではなく、なぜクラスを使用するのですか? 基本的なレベルでは、クラスを使用してコードとデータを論理ユニットに編成します。 しかし、それだけではありません。 クラスを使用すると、抽象化を作成できます 。 抽象化とは何ですか? 毎日、1分ごとに抽象化を使用します。 キーボード 私はこれを入力しています: ケーブル チェリーMXブラックスイッチ すべてをまとめるプラスチックケース 電気信号をコンピュータ