متى يتم تعيين متغيرات مثيل روبي؟
-
05-07-2019 - |
سؤال
class Hello
@hello = "hello"
def display
puts @hello
end
end
h = Hello.new
h.display
لقد قمت بإنشاء الفصل أعلاه.لا يطبع أي شيء.اعتقدت أنه تم تعيين متغير المثيل @hello أثناء إعلان الفصل.ولكن عندما أستدعي طريقة العرض يكون الإخراج "لا شيء".ما هي الطريقة الصحيحة للقيام بذلك؟
المحلول
قد تكون متغيرات الحالة في روبي مربكة بعض الشيء عند تعلم روبي لأول مرة، خاصة إذا كنت معتادًا على لغة OO أخرى مثل Java.
لا يمكنك ببساطة الإعلان عن متغير مثيل.
أحد أهم الأشياء التي يجب معرفتها حول متغيرات الحالة في روبي، بصرف النظر عن التدوين الذي يحتوي على بادئة علامة @، هو أنه إنهم ينبضون بالحياة في المرة الأولى التي يتم فيها تكليفهم بذلك.
class Hello
def create_some_state
@hello = "hello"
end
end
h = Hello.new
p h.instance_variables
h.create_some_state
p h.instance_variables
# Output
[]
["@hello"]
يمكنك استخدام الطريقة Object#instance_variables
لسرد كافة متغيرات مثيل الكائن.
عادةً ما تقوم "بإعلان" كافة متغيرات المثيلات وتهيئتها في طريقة التهيئة.هناك طريقة أخرى للتوثيق الواضح لمتغيرات الحالة التي يجب أن تكون متاحة للعامة وهي استخدام أساليب الوحدة النمطية attr_accessor
(قراءة و كتابة)، attr_writer
(اكتب و attr_reader
(يقرأ).ستقوم هذه الطرق بتجميع طرق وصول مختلفة لمتغير المثيل المدرج.
class Hello
attr_accessor :hello
end
h = Hello.new
p h.instance_variables
h.hello = "hello"
p h.instance_variables
# Output
[]
["@hello"]
لا يزال لم يتم إنشاء متغير المثيل حتى يتم تعيينه لاستخدام ملف Synthesized Hello#hello=
طريقة.
هناك مشكلة أخرى مهمة، كما وصفها kch، وهي أنك بحاجة إلى أن تكون على دراية بالسياقات المختلفة النشطة عند الإعلان عن الفصل الدراسي.عند الإعلان عن فئة جهاز الاستقبال الافتراضي (الذاتي) في النطاق الخارجي سيكون الكائن الذي يمثل الفئة نفسها.ومن ثم، سيقوم الكود الخاص بك أولاً بإنشاء متغير مثيل للفئة عند التعيين إلى @hello
على مستوى الصف.
الأساليب الداخلية الذات سيكون الكائن الذي يتم استدعاء الطريقة عليه، ومن ثم فإنك تحاول طباعة قيمة متغير مثيل بالاسم @hello
في الكائن، وهو غير موجود (لاحظ أنه من القانوني تمامًا قراءة متغير مثيل غير موجود).
نصائح أخرى
وتحتاج إلى إضافة وسيلة initialize
:
class Hello
def initialize
@hello = "hello"
end
def display
puts @hello
end
end
h = Hello.new
h.display
ويسمى @hello
أولا في التعليمات البرمجية متغير مثيل فئة.
وانها متغير مثيل كائن الفئة التي يشير Hello
المستمرة ل. (والذي هو مثيل Class
الدرجة).
ومن الناحية الفنية، عندما كنت في نطاق class
، يتم تعيين self
بك إلى كائن من الدرجة الحالية، و@variables
تتعلق إلى self
الحالية الخاصة بك. صبي أمتص في شرح هذه الأمور.
ويمكنك الحصول على كل هذا وأكثر من ذلك بكثير أوضح لك بمشاهدة <لأ href = "http://www.pragprog.com/screencasts/v-dtrubyom/the-ruby-object-model-and-metaprogramming" يختلط = "noreferrer"> هذه المجموعة من $ 5 لكل قطات للشاشة من المبرمجون عملي .
و(أو يمكنك أن تطلب توضيحات هنا وسأحاول لتحديث.)
هناك وصف واضح في كتاب "لغة برمجة الياقوت"، اقرأه سيكون مفيدًا جدًا.ألصقه هنا (من الفصل 7.1.16):
متغير مثيل يستخدم داخل تعريف فئة ولكن خارج تعريف أسلوب المثيل هو متغير مثيل الفئة.
class Point
# Initialize our class instance variables in the class definition itself
@n = 0 # How many points have been created
@totalX = 0 # The sum of all X coordinates
@totalY = 0 # The sum of all Y coordinates
def initialize(x,y) # Initialize method
@x,@y = x, y # Sets initial values for instance variables
end
def self.new(x,y) # Class method to create new Point objects
# Use the class instance variables in this class method to collect data
@n += 1 # Keep track of how many Points have been created
@totalX += x # Add these coordinates to the totals
@totalY += y
super # Invoke the real definition of new to create a Point
# More about super later in the chapter
end
# A class method to report the data we collected
def self.report
# Here we use the class instance variables in a class method
puts "Number of points created: #@n"
puts "Average X coordinate: #{@totalX.to_f/@n}"
puts "Average Y coordinate: #{@totalY.to_f/@n}"
end
end
......
لأن متغيرات مثيل الفئة هي مجرد متغيرات مثيل للفئة الكائنات ، يمكننا استخدام ATTR و attr_reader و attr_accessor لإنشاء طرق الوصول لهم.
class << self
attr_accessor :n, :totalX, :totalY
end
بعد تحديد هذه الموصلات، يمكننا الإشارة إلى بياناتنا الأولية باسم Point.n وPoint.totalX وPoint.totalY.
لقد نسيت أن هناك مفهوم "متغير مثيل الفئة" في روبي.على أي حال، بدت مشكلة OP محيرة، ولم يتم تناولها حقًا في أي من الإجابات حتى الآن، باستثناء تلميح في إجابة kch:إنها مشكلة النطاق.(أضيفت عند التعديل:في الواقع، إجابة sris يفعل قم بمعالجة هذه النقطة في النهاية، لكنني سأترك هذه الإجابة قائمة على أي حال، حيث أعتقد أن رمز المثال قد يكون مفيدًا لفهم المشكلة.)
في فئة روبي، اسم متغير يبدأ بـ @
يمكن الرجوع إلى واحد من اثنين المتغيرات:إما إلى المتغيرات الخاصة أو إلى أ متغير مثيل الفئة, ، اعتمادًا على مكان الفصل المشار إليه.هذا هو مسكتك خفية إلى حد ما.
مثال سوف يوضح هذه النقطة.إليك فئة اختبار روبي صغيرة (تم اختبار جميع الأكواد في irb):
class T
@@class_variable = "BBQ"
@class_instance_variable_1 = "WTF"
@class_instance_variable_2 = "LOL"
def self.class_method
puts "@@class_variable == #{@@class_variable || 'nil'}"
puts "@class_instance_variable_1 == #{@class_instance_variable_1 || 'nil'}"
puts "@class_instance_variable_2 == #{@class_instance_variable_2 || 'nil'}"
puts "@instance_variable == #{@instance_variable || 'nil'}"
end
def initialize
@instance_variable = "omg"
# The following line does not assign a value to the class instance variable,
# but actually declares an instance variable withthe same name!
@class_instance_variable_1 = "wtf"
puts "@@class_variable == #{@@class_variable || 'nil'}"
# The following two lines do not refer to the class instance variables,
# but to the instance variables with the same names.
puts "@class_instance_variable_1 == #{@class_instance_variable_1 || 'nil'}"
puts "@class_instance_variable_2 == #{@class_instance_variable_2 || 'nil'}"
puts "@instance_variable == #{@instance_variable || 'nil'}"
end
def instance_method
puts "@@class_variable == #{@@class_variable || 'nil'}"
# The following two lines do not refer to the class instance variables,
# but to the instance variables with the same names.
puts "@class_instance_variable_1 == #{@class_instance_variable_1 || 'nil'}"
puts "@class_instance_variable_2 == #{@class_instance_variable_2 || 'nil'}"
puts "@instance_variable == #{@instance_variable || 'nil'}"
end
end
لقد قمت بتسمية المتغيرات وفقًا لما اعتقدت أنها كذلك، على الرغم من أن هذا ليس هو الحال دائمًا:
irb> T.class_method
@@class_variable == BBQ
@class_instance_variable_1 == WTF # the value of the class instance variable
@class_instance_variable_2 == LOL # the value of the class instance variable
@instance_variable == nil # does not exist in the class scope
=> nil
irb> t = T.new
@@class_variable == BBQ
@class_instance_variable_1 == wtf # the value of the instance variable
@class_instance_variable_2 == nil # the value of the instance variable
@instance_variable == omg
=> #<T:0x000000015059f0 @instance_variable="omg", @class_instance_variable_1="wtf">
irb> t.instance_method
@@class_variable == BBQ
@class_instance_variable_1 == wtf # the value of the instance variable
@class_instance_variable_2 == nil # the value of the instance variable
@instance_variable == omg
=> nil
irb> T.class_method
@@class_variable == BBQ
@class_instance_variable_1 == WTF # the value of the class instance variable
@class_instance_variable_2 == LOL # the value of the class instance variable
@instance_variable == nil # does not exist in the class scope
=> nil
ال @@class_variable
و @instance_variable
تصرف دائمًا كما تتوقع:يتم تعريف الأول على مستوى الفصل، وسواء تمت الإشارة إليه في أسلوب الفصل أو في أسلوب المثيل، فإنه يحمل القيمة المخصصة له في الأعلى.هذا الأخير يحصل فقط على قيمة في كائن من الفئة T
, ، لذلك في طريقة الفصل، يشير إلى متغير غير معروف قيمته nil
.
تم تسمية طريقة الفصل بشكل خيالي class_method
إخراج قيم @@class_variable
واثنين @class_instance_variable
كما هو متوقع، أي كما تمت تهيئته في أعلى الفصل الدراسي.ومع ذلك، في أساليب المثال initialize
و instance_method
, مختلف المتغيرات من نفس الاسم يتم الوصول إليها، أي متغيرات المثيل، وليس متغيرات مثيل الفئة.
يمكنك أن ترى أن المهمة في initialize
لم تؤثر الطريقة على متغير مثيل الفئة @class_instance_variable_1
, ، لأن الدعوة في وقت لاحق من class_method
يخرج قيمته القديمة ، "WTF"
.بدلا من ذلك، الأسلوب initialize
تم الإعلان عن متغير مثيل جديد، واحد وهو أيضًا اسمه (مضلل) @class_instance_variable_1
.القيمة المخصصة له، "wtf"
, ، يتم الإخراج بالطرق initialize
و instance_method
.
المتغير @class_instance_variable_2
في رمز المثال يعادل المتغير @hello
في المشكلة الأصلية:تم الإعلان عنه وتهيئته كمتغير مثيل للفئة، ولكن عندما تشير طريقة المثيل إلى متغير بهذا الاسم، فإنه يرى فعليًا متغير مثيل بنفس الاسم - لم يتم التصريح عنها مطلقًا، فقيمتها صفر.
وأوصى أيضا النظر في المتغيرات الطبقة التي تكون مسبوقة مع "" - وهنا بعض التعليمات البرمجية لتبين لكم كيف الفئة و المثيل فار مختلفة:
class Vars
@@classvar="foo"
def test
@instancevar="bar"
end
def Vars.show
puts "classvar: #{@@classvar}"
puts "instancevar: #{@instancevar}"
end
def instance_show
puts "classvar: #{@@classvar}"
puts "instancevar: #{@instancevar}"
end
end
# only shows classvar since we don't have an instance created
Vars::show
# create a class instance
vars = Vars.new
# instancevar still doesn't show b/c it hasn't been initialized
vars.instance_show
# initialize instancevar
vars.test
# now instancevar shows up as we expect
vars.instance_show