دلالات المؤشر والقيمة في تحديد مستقبل إحدى الطرق

يعد إنشاء أنواع بيانات جديدة جزءًا مهمًا من عمل كل مبرمج. في معظم اللغات ، يتكون تعريف النوع من وصف للحقول والأساليب الخاصة به. في Golang ، بالإضافة إلى ذلك ، تحتاج إلى تحديد دلالات المستلم للطرق من النوع الجديد لاستخدام: القيمة (القيمة) أو المؤشر (المؤشر). للوهلة الأولى ، قد يبدو هذا القرار ثانويًا ، لأنه في معظم الحالات ، سيعمل البرنامج مع أي دلالات للمتلقي. لذلك ، يتخطى الكثير من الناس هذه النقطة ويكتبوا الكود ، ولم يتوصلوا إليها حتى النهاية ، والتي تتأثر بدلالات مستلم الطريقة. وللتعرف على ذلك ، أنت بحاجة إلى التعمق أكثر في طريقة عمل Golang.

النظر في مثال صغير. تحديد هيكل القط مع حقل اسم واحد وطريقة sayHello (سلسلة شخص) . فيما يلي ، من خلال طريقة سأشير إلى دالة مرتبطة بنوع معين ، كائن إلى متغير له طرق ، وسيكون متلقي الطريقة هو المتغير المشار إليه بين قوسين بعد كلمة func في وصف الطريقة.

type cat struct { Name string } func (c *cat) sayHello(person string) { fmt.Println(fmt.Sprintf("Meow, meow, %s!", person) } 

إذا حددنا مؤشرًا للقطط وطلب حقل الاسم منه ، فمن الواضح أننا سنحصل على خطأ ، حيث يتم استدعاء الحقل من الصفر :

 var c *cat // c=nil fmt.Println(c.Name) //panic: runtime error: invalid memory address or nil pointer dereference 

https://play.golang.org/p/L3FnRJXKqs0

ومع ذلك ، عندما يتم استدعاء الأسلوب sayHello () على نفس المتغير ، لن يكون هناك أي خطأ:

 var c *cat // c=nil c.sayHello(“Human”) //Meow, meow, Human! 

https://play.golang.org/p/EMoFgKL1HEi

لماذا لا يمكن استدعاء طريقة في هذا المثال ، وكيف يتم تفسير ذلك من حيث بنية اللغة نفسها؟ يصبح هذا ممكنًا لأن الطريقة في Go هي السكر النحوي ، أو ، بمعنى آخر ، غلاف حول دالة بها إحدى وسائط المتلقي. عندما يتم استدعاء طريقة c.sayHello ("Human") ، سيتم بالفعل استدعاء (* cat) .sayHello (c، s) ( https://play.golang.org/p/X9leJeIvxcA ). عن طريق استدعاء أسلوب nil من المثال أعلاه ، فإننا نسميها فعليًا الدالة nil في الوسائط ، وهذا بالفعل وضع طبيعي تمامًا. لذلك ، في Go nil ، هو المستلم الصحيح للطرق.

نظرًا لأن مستقبل الأسلوب عبارة عن وسيطة ، فإن توصيات استخدام الدلالة "قيمة" أو "المؤشر" لمستقبل الطريقة تشبه توصيات الوسيطات الوظيفية. وهي ، بدورها ، يتم استنتاجها من قاعدة Go الأساسية: يتم دائمًا تمرير الوسائط إلى الدالة حسب القيمة . هذا يعني أن نقل أي وسيطة إلى الوظيفة يحدث من خلال نسخها: إذا قبلت الدالة بنية كمدخلات ، فستظهر نسخة كاملة من هذه البنية داخلها ؛ إذا أخذ مؤشر إلى كائن ، فسيأتي متغير جديد مع مؤشر إلى نفس الكائن. يمكن ملاحظة ذلك من خلال مقارنة العنوان المتغير قبل تمريره إلى الوظيفة مع عنوان الوسيطة داخل الوظيفة ( https://play.golang.org/p/oc2ssC_Irs8 ، https://play.golang.org/p/FeQa2HUdX0a ).

عند استخدام تمرير الرابط:

  • للهياكل الكبيرة. يحتل المؤشر كلمة آلة واحدة فقط (32 ، 64 بت حسب النظام). لذلك ، عند استدعاء طريقة مع مؤشر في المتلقي ، فإن نسخ المؤشر أرخص من نسخ الكائن بأكمله ، كما هو الحال من خلال تمرير القيمة.
  • إذا كانت الطريقة التي تم استدعاؤها تعدل بيانات الكائن نفسه. عندما يتم نقل المستلم بالرجوع إليه ، يمكن أن تؤثر الطريقة على حالة كائن الاستدعاء عن طريق إجراء تغييرات غير مباشرة. وهو أمر مستحيل عند المرور بالقيمة.

عند استخدام نقل القيمة:

  • لأنواع مدمجة بسيطة مثل الأرقام والسلاسل ، منطقي. عند استخدام المؤشر ، يتم استخدام نفس مقدار الذاكرة تقريبًا مثل كائن من هذا النوع ، وتزداد تكلفة صيانته بواسطة أداة تجميع مجمعي البيانات المهملة ، كما هو موضح أدناه.
  • بالنسبة للشرائح ، وكذلك الأنواع المرجعية الأخرى: الخريطة والقنوات - ليس من المنطقي أن تأخذ المؤشر. هم أنفسهم بالفعل مؤشر.
  • مع تعدد الخيوط ، يكون المرور حسب القيمة آمنًا ، على عكس المرور حسب المرجع.
  • للهياكل الصغيرة. في مثل هذه الحالات ، يكون النقل حسب القيمة أكثر كفاءة. هذا لأنه يتم وضع البيانات الداخلية للطرق في إطار منفصل من المكدس. بعد الخروج من الوظيفة ، يتم مسح إطارها. عندما نفتش شيئًا ما على طول المؤشر ، ننقل هذه البيانات من المكدس إلى الكومة ، حيث قد تكون هذه البيانات متاحة لوظائف أخرى. تؤدي زيادة الكومة إلى إنشاء عبء إضافي على أداة تجميع مجمعي البيانات المهملة ، والتي تؤدي عملياتها إلى تقليل سرعة البرنامج بمعدل 25٪. عند استخدام نقل القيمة حسب القيمة ، تبقى البيانات في الحزمة ولا يلزم عمل أداة تجميع مجمعة بيانات إضافية.

عندما تحتاج إلى التفكير في دلالات المستلم:

  • قد يختلف نوع المستلم حسب مجال الموضوع. في إحدى خطبه ، أعطى بيل كينيدي مثالاً جيدًا على نوع المستخدم الذي يصف المستخدم. عندما يتم تمريرها من خلال القيمة ، سيتم إنشاء نسخة للمستخدم. سيؤدي ذلك إلى حقيقة أن عدة نسخ من نفس المستخدم يمكن أن تتعايش في البرنامج في نفس الوقت ، والتي يمكن بعد ذلك تغييرها بشكل مستقل ، والتي لا تتوافق مع موضوع الموضوع ، لأن المستخدم الحقيقي هو دائمًا نسخة واحدة ، ولا يمكن وصفه في أوقات مختلفة بواسطة مجموعات مختلفة البيانات.
  • هناك طريقة أخرى مؤكدة لتحديد نوع المستلم لطريقة ما وهي استخدام طريقة المنشئ لنوعها. إذا قام المُنشئ بإرجاع قيمة / مؤشر ، فعند إنشاء كيان ، يُفترض أنه سيواصل العمل معه كقيمة / مؤشر. لذلك ، من الأفضل أيضًا استخدام نفس الدلالات في مستقبل الأسلوب.
  • هناك قاعدة غير مكتوبة ، والتي لن ينتهك فيها المترجم ، ولكن لن تتحسن الشفرة الخاصة بك بالتأكيد من هذا. إذا كانت إحدى طرق الكتابة تستخدم مؤشر / قيمة كمستقبل ، ثم للحفاظ على الاتساق ، يجب أن تستخدم الطرق المتبقية مؤشر / قيمة. يجب ألا تحتوي طرق الكتابة على تجزئة لمستقبلات القيمة والمؤشرات.

ما هي النتيجة


في Go Value ، تعني الدلالات: نسخ قيمة ؛ تعني دلالات المؤشر إعطاء الوصول إلى قيمة. وهذا ينطبق على كل من وسيطات الأساليب وعلى مستلميها. بالنسبة للأنواع المضمنة ، مثل الأرقام والخطوط والشرائح والخرائط والقنوات والهياكل الصغيرة ، يلزمك دائمًا استخدام النقل المستند إلى القيمة. بالنسبة إلى الهياكل التي تشغل مساحة كبيرة من الذاكرة ، والهياكل التي يمكن تغيير حالتها بشكل غير مباشر بطرقها ، يجب عليك استخدام النقل حسب المرجع. أيضًا ، قد تعتمد دلالات المستلم على مجال الموضوع الذي يصفه النوع ، والدلالات التي يتم إرجاعها في المصنع ، ودلالات المستلم المستخدمة بالفعل في طرق أخرى من هذا النوع.

Source: https://habr.com/ru/post/ar469859/


All Articles