۱۰ آذر ۱۴۰۴
با reflection در گولنگ آشنا میشیم و یاد میگیریم چطور از رفلکشن استفاده کنیم.
reflection یا رِفلِکشِن در برنامهنویسی قابلیتی است که به یک برنامه اجازه میدهد در زمان اجرا (runtime) اطلاعاتی دربارهی ساختار خودش مثل دیتا تایپ ها (types)، متغیرها و تابع ها به دست بیاورد، و حتی آنها را تغییر بدهد یا فراخوانی کند. به بیان ساده تر رِفلِکشِن (reflection) یعنی برنامه بتواند در زمان اجرا, خودش را بررسی یا دستکاری کند.
رِفلِکشِن معمولاً کندتر است و ممکن است ساختار و فهم کد را پیچیدهتر کند. به همین دلیل پیشنهاد میشود فقط وقتی لازم است از آن استفاده شود.
برای محتوای این پست از The laws of reflection استفاده و یکسری نکات تکمیلی به آن اضافه کردیم.
در Go با استفاده از پکیج reflect میتوان از قابلیت رفلکشن استفاده کرد. این پکیج حول سه مفهوم کلی شکل گرفته است:
kind
type
value
برای اینکه معنی هرکدام از این مفاهیم را درک کنیم بیاید از یک مثال استفاده کنیم:
در مثال بالا سه متغیر x, y و z را تعریف و مقداردهی کردیم و سپس با استفاده از رفلکشن value, type و kind آنها را در صفحه چاپ کردیم.
x دارای value برابر 10 با است. type آن int و kind آن نیز int است.y دارای value برابر با [2, 4, 6] است. type آن [ ]int و kind آن slice است.z دارای value برابر با {Barfi 3} است. type آن Barfi و kind آن struct است.type تعریف دقیق یک نوعِ داده را مشخص میکند که شامل نام دیتاتایپ و جزییات ساختاری آن میشود در حالی که kind فقط ساختار پایهی نوع را مشخص میکند.
در مثال بالا دو نوعِ دادهی Time و Duration را تعریف کردیم. این دو نوعِ داده دارای type های متفاوت هستند اما kind هردوی آنها int64 است. بنابراین حتی اگر دادههای کاملا یکسانی داشته باشیم اگر نوعِ دادهی آنها اسم متفاوتی داشته باشد، Type آنها متفاوت است.
رفلکشن در زبان Go حول محور سه قانون شکل گرفته است:
از طریق interface میتوانید به آبجکت رفلکشن دست پیدا کنید.
از طریق آبجکت رفلکشن میتوانید به interface دست پیدا کنید.
برای تغییرات بر روی یک آبجکت رفلکشن باید آن آبجکت قابلیت تغییر داده شدن داشته باشد.
دو نوع آبجکت رفلکشن وجود دارد. یکی از نوع Value و دیگری از نوع Type است. از طریق متد reflect.ValueOf میتوان به Value یک اینترفیس و به صورت مشابه از طریق متد reflect.TypeOf میتوان به Type و Kind یک اینترفیس دسترسی داشت.
از طریق آبجکت reflect.Value میتوان به اینترفیس دسترسی داشت. در مثال زیر یک آبجکت reflect.Value را به نوع اولیه داده که همان [ ]int است تبدیل میکنیم.
بنابراین همانند مثال بالا, از طریق متد Interface میتوان به اینترفیس اصلی دست پیدا کرد و سپس با type assertion آن مقدار را به مقدار اولیه تبدیل کرد.
آبجکت reflect.Value دارای متدهایی است که نام آنها با Set شروع میشود. برای مثال SetInt یکی از آنها است. با توجه به نوع داده اصلی میتوانید توسط این متدها مقدار داده را تغییر دهید اما قبل از اینکار باید اطمینان داشته باشید که آبجکت رفلکشن قابلیت تغییر داده شدن دارد!
کد بالا با خطا مواجهه خواهد شد زیرا با اجرای SetFloat یک panic رخ میدهد و روند اجرای برنامه خاتمه پیدا میکند. این خطا به این دلیل رخ میدهد که آبجکت ما قابلیت ست شدن ندارد. با متد CanSet میتوان قابلیت تغییر داده شدن یک آبجکت را بررسی کرد.
ست کردن (Set) یک مقدار بر روی آبجکتی که غیر قابل ست شدن باشد باعث بروز خطا (panic) میشود.
برای اینکه آبجکتی قابل ست شدن داشته باشیم باید یک اشاره گر به تابع ValueOf پاس دهیم (با استفاده از &) و سپس با استفاده از متد Elem میتوانیم به مقداری که اشاره گر به آن اشاره میکند دسترسی داشته باشم و آن را دهیم:
همچنین میتوانیم از متد Set نیز استفاده کنیم:
مقداری که بر روی متغیر ست میکنیم باید دارای نوع داده یکسانی باشد در غیر اینصورت اجرای برنامه با خطا مواجهه خواهد شد.
یکی از توابع خیلی کاربردی که میتوان درون پکیج reflect پیدا کرد, DeepEqual نام دارد. این تابع دو پارامتر ورودی میگیرد, در صورتی که مقدار این دو پارامتر برابر باشند true و در غیر اینصورت false برمیگرداند.
پیشنهاد میکنم یه نگاه اجمالی به لیست توابع پکیج reflect داشته باشید.
قسمت قبل: پکیج و ماژول | گولنگ به زبان ساده
قسمت بعد: به زودی…