فصل دوازدهم: استفاده از ابزارها برای بهبود کیفیت کد 🛠️
به عنوان یک برنامهنویس، ارتقای کیفیت کد یکی از مهمترین دغدغههای شماست. بهبود کیفیت کد نیازمند استفاده از ابزارهای مختلف است. ابزارهایی که برای بهبود کد و همچنین سرعت بخشیدن به توسعه طراحی شدهاند، شامل code metrics، quick actions، پروفایلر JetBrains dotTrace، JetBrains ReSharper و Telerik JustDecompile هستند.
در این فصل، ما عمدتاً روی موارد زیر تمرکز خواهیم کرد:
- تعریف کد با کیفیت بالا
- انجام پاکسازی کد و محاسبه code metrics
- انجام تحلیل کد
- استفاده از quick actions
- استفاده از پروفایلر JetBrains dotTrace
- استفاده از JetBrains ReSharper
- استفاده از Telerik JustDecompile
در پایان این فصل، شما مهارتهای زیر را کسب خواهید کرد:
- استفاده از code metrics برای اندازهگیری پیچیدگی و نگهداریپذیری نرمافزار
- استفاده از quick actions برای اعمال تغییرات با یک دستور
- پروفایلینگ کد و تحلیل گلوگاههای عملکرد با JetBrains dotTrace
- بازسازی (Refactoring) کد با استفاده از JetBrains ReSharper
- دیکامپایل کردن کد و تولید یک راهحل (solution) با استفاده از Telerik JustDecompile
نیازمندیهای فنی 💻
- کد منبع این کتاب: GitHub
- Visual Studio 2019 Community Edition یا بالاتر: دانلود
- Telerik JustDecompile: سایت رسمی
- JetBrains ReSharper Ultimate: دانلود
تعریف کد با کیفیت بالا ✅
کیفیت بالای کد یک ویژگی اساسی نرمافزار است. کد با کیفیت پایین میتواند منجر به ضرر مالی، اتلاف وقت و تلاش، و حتی خطرات جانی شود. کد با استاندارد بالا باید ویژگیهای زیر را داشته باشد: Performance, Availability, Security, Scalability, Maintainability, Accessibility, Deployability, Extensibility (PASSMADE)
-
Performance (عملکرد): کد پرسرعت، کوچک، و تنها کاری که لازم است انجام میدهد. چنین کدی سیستم را متوقف نمیکند. عواملی که سیستم را متوقف میکنند شامل عملیات ورودی/خروجی (I/O)، مصرف حافظه، و استفاده از پردازنده (CPU) هستند. کد با عملکرد پایین نیاز به Refactoring دارد.
-
Availability (دسترسپذیری): نرمافزار باید همواره در سطح عملکرد مورد نیاز در دسترس باشد. دسترسپذیری نسبت زمانی که نرمافزار عملکرد دارد (tsf) به زمان کل مورد انتظار برای عملکرد (ttef) است. مثال: tsf = 700; ttef = 744 → 700 / 744 = 0.9409 = 94.09٪ دسترسپذیری
-
Security (امنیت): کد امن ورودیها را به درستی اعتبارسنجی میکند تا از فرمتهای دادهای نامعتبر، محدودههای دادهای نامعتبر، و حملات مخرب جلوگیری شود و کاربران را به طور کامل احراز هویت و مجوزدهی کند. کد امن همچنین Fault-tolerant است؛ برای مثال اگر انتقال پول نیمهتمام مانده و سیستم خراب شود، دادهها باید سالم باقی بمانند و پولی از حساب کم نشود.
-
Scalability (قابلیت مقیاسپذیری): کد باید بتواند رشد نمایی تعداد کاربران را بدون کاهش عملکرد یا توقف سیستم مدیریت کند. چه نرمافزار یک درخواست در ساعت پردازش کند یا یک میلیون درخواست، عملکرد نباید کاهش یابد و Downtime ایجاد نشود.
-
Maintainability (نگهداریپذیری): آسان بودن رفع باگها و اضافه کردن ویژگیهای جدید. کد قابل نگهداری باید منظم، خوانا، با low coupling و high cohesion باشد تا به راحتی قابل نگهداری و توسعه باشد.
-
Accessibility (قابلیت دسترسی): کد باید برای افراد با توانایی محدود نیز آسان برای استفاده و تغییر باشد. مثال: رابطهای کاربری با کنتراست بالا، Narrator برای افراد دیسلکسیک یا نابینا و غیره.
-
Deployability (قابلیت پیادهسازی): نرمافزار باید برای انواع کاربران—چه Standalone، Remote Access، یا Local Network—به راحتی قابل پیادهسازی باشد.
-
Extensibility (قابلیت توسعهپذیری): آسان بودن افزودن ویژگیهای جدید. کدهای Spaghetti و کدهای با Coupling بالا و Cohesion پایین باعث دشواری و احتمال خطای زیاد میشوند. کد extensible باید خوانا، قابل نگهداری و آسان برای افزودن ویژگیهای جدید باشد.
از ویژگیهای PASSMADE میتوان مشکلات احتمالی ناشی از عدم رعایت این استانداردها را پیشبینی کرد:
- کد کند و ناامیدکننده
- افزایش Downtime برای کاربران
- سوءاستفاده هکرها از آسیبپذیریها
- کاهش عملکرد نرمافزار با افزایش کاربران
- سخت بودن رفع باگ یا توسعه کد
- کاربران با توانایی محدود نمیتوانند نرمافزار را متناسب با نیاز خود تغییر دهند
- پیادهسازی نرمافزار تبدیل به یک کابوس پیکربندی میشود
Code Metrics به کمک میآید 📏
Code metrics به توسعهدهندگان امکان میدهد پیچیدگی و نگهداریپذیری کد را اندازهگیری کنند و کدهایی که نیاز به Refactoring دارند را شناسایی کنند.
- Quick Actions: با یک دستور میتوانید کد C# را Refactor کنید، مثلاً استخراج کد به متد جداگانه.
- JetBrains dotTrace: کد شما را پروفایل میکند و Bottleneckهای عملکرد را پیدا میکند.
- JetBrains ReSharper: افزونهای برای Visual Studio که کیفیت کد را تحلیل، Code Smells را شناسایی، استانداردهای برنامهنویسی را اعمال و کد را Refactor میکند.
- Telerik JustDecompile: به شما امکان Decompile کردن کد موجود برای رفع مشکل یا ایجاد پروژههای IL، C# و VB.NET را میدهد. این ابزار مخصوصاً زمانی مفید است که Source Code در دسترس نباشد و نیاز به نگهداری یا توسعه کد کامپایلشده داشته باشید. حتی میتوانید Debug Symbols برای کد کامپایلشده تولید کنید.
در ادامه، به بررسی دقیقتر ابزارها خواهیم پرداخت، ابتدا با Code Metrics شروع میکنیم. ✅
انجام پاکسازی کد و محاسبه Code Metrics 🧹📏
قبل از آنکه ببینیم چگونه code metrics را جمعآوری کنیم، ابتدا باید بدانیم که این معیارها چه هستند و چرا برای ما مفیدند. Code metrics عمدتاً به پیچیدگی نرمافزار و نگهداریپذیری آن مربوط میشوند. این معیارها به ما کمک میکنند ببینیم چگونه میتوانیم نگهداری کد منبع را بهبود بخشیم و پیچیدگی کد را کاهش دهیم.
معیارهای کد که Visual Studio 2019 محاسبه میکند، شامل موارد زیر است
۱. Maintainability Index (شاخص نگهداریپذیری)
نگهداری کد یک بخش ضروری از Application Lifecycle Management (ALM) است. تا زمانی که نرمافزار به پایان عمر خود نرسد، باید نگهداری شود. هر چه نگهداری کد دشوارتر باشد، طول عمر کد منبع قبل از نیاز به جایگزینی کامل کوتاهتر است.
نوشتن نرمافزار جدید برای جایگزینی یک سیستم مشکل و هزینهبر، بسیار بیشتر از نگهداری یک سیستم موجود است. شاخصی که برای اندازهگیری نگهداریپذیری کد استفاده میشود، Maintainability Index نام دارد. این مقدار یک عدد صحیح بین ۰ تا ۱۰۰ است.
ردهبندی Maintainability Index، رنگها و معانی آنها:
- هر مقداری از ۲۰ و بالاتر: رنگ سبز، یعنی نگهداری آسان ✅
- کد با نگهداری متوسط: بین ۱۰ تا ۱۹، رنگ زرد ⚠️
- هر مقداری کمتر از ۱۰: رنگ قرمز 🔴، یعنی نگهداری دشوار
۲. Cyclomatic Complexity (پیچیدگی سیکلوماتیک)
پیچیدگی کد، که به آن Cyclomatic Complexity نیز گفته میشود، به تعداد مسیرهای مختلف کد در نرمافزار اشاره دارد. هر چه مسیرها بیشتر باشند، نرمافزار پیچیدهتر است. هر چه نرمافزار پیچیدهتر باشد، تست و نگهداری آن دشوارتر میشود.
کد پیچیده میتواند باعث افزایش خطاها در نسخههای نرمافزار و سخت شدن نگهداری و توسعه شود. بنابراین، توصیه میشود پیچیدگی کد حداقل نگه داشته شود.
۳. Depth of Inheritance (عمق ارثبری)
معیار Depth of Inheritance و Class Coupling تحت تأثیر Object-Oriented Programming (OOP) قرار دارند. در OOP، کلاسها میتوانند از کلاسهای دیگر ارثبری کنند.
- Base Class (کلاس پایه): کلاسی که از آن ارثبری میشود
- Subclass (زیرکلاس): کلاسهایی که از کلاس پایه ارث میبرند
عمق ارثبری، تعداد کلاسهایی است که از یکدیگر ارث میبرند. هر چه عمق ارثبری بیشتر باشد، احتمال خطا در کلاسهای مشتقشده هنگام تغییر در کلاسهای پایه بیشتر است. عمق ارثبری ایدهآل = ۱
۴. Class Coupling (وابستگی کلاسها)
OOP امکان Coupling کلاسها را فراهم میکند. وابستگی کلاس زمانی رخ میدهد که یک کلاس مستقیماً توسط پارامتر، متغیر محلی، نوع بازگشتی، فراخوانی متد، نمونهسازی Generic یا Template، کلاسهای پایه، پیادهسازی Interface، فیلدهای تعریفشده روی نوعهای اضافی و Attribute Decoration ارجاع داده شود.
معیار Class Coupling سطح وابستگی بین کلاسها را تعیین میکند. برای نگهداری و توسعه آسانتر کد، Class Coupling باید حداقل باشد.
در OOP، یک راه برای دستیابی به این هدف استفاده از Interface-based Programming است. این روش امکان تعویض کلاسها را فراهم میکند، به شرطی که همان Interface را پیادهسازی کنند.
- کد با کیفیت پایین: Coupling بالا و Cohesion پایین
- کد با کیفیت بالا: Coupling پایین و Cohesion بالا ✅
نکته: نرمافزار باید Cohesion بالا و Coupling پایین داشته باشد، چون تست، نگهداری و توسعه آن را آسانتر میکند.
۵. Lines of Source Code (تعداد خطوط کد منبع)
تعداد کل خطوط کد منبع شما، شامل خطوط خالی، با این معیار اندازهگیری میشود.
۶. Lines of Executable Code (تعداد خطوط کد اجرایی)
تعداد عملیات در کد اجرایی با این معیار سنجیده میشود.
شروع استفاده از Code Metrics در Visual Studio 2019 🖥️
حالا که با Code Metrics و معیارهای موجود در Visual Studio 2019 نسخه 16.4 به بعد آشنا شدید، وقت آن است که آنها را عملی ببینیم:
1️⃣ هر پروژهای که میخواهید در Visual Studio باز کنید
2️⃣ روی پروژه راستکلیک کنید
3️⃣ گزینه Analyze and Code Cleanup | Run Code Cleanup (Profile 1) را انتخاب کنید، همانطور که در تصویر زیر نشان داده شده است
4️⃣ اکنون گزینه Calculate Code Metrics را انتخاب کنید.
5️⃣ باید پنجره Code Metrics Results ظاهر شود، همانطور که در تصویر زیر نشان داده شده است. ✅
همانطور که در تصویر مشاهده میکنید، تمام کلاسها، اینترفیسها و متدها با شاخص سبز علامتگذاری شدهاند. این بدان معناست که پروژه انتخابشده قابل نگهداری است. اگر هر یک از این خطوط با رنگ زرد یا قرمز مشخص شده بودند، باید آنها را بررسی کرده و Refactor کنید تا به رنگ سبز درآیند.
خب، حالا که با Code Metrics آشنا شدیم، طبیعتاً به سراغ Code Analysis میرویم. 🔍
انجام تحلیل کد (Performing Code Analysis) 🛠️
برای کمک به توسعهدهندگان در شناسایی مشکلات بالقوه در کد منبع، شرکت Microsoft ابزار Code Analysis را به عنوان بخشی از Visual Studio ارائه کرده است.
Code Analysis یک تحلیل Static روی کد منبع انجام میدهد و میتواند موارد زیر را شناسایی کند:
- مشکلات طراحی (Design Flaws)
- مشکلات مربوط به Globalization
- مشکلات امنیتی (Security Problems)
- مسائل مربوط به عملکرد (Performance Issues)
- مشکلات Interoperability
برای استفاده از این ابزار:
1️⃣ راهحل کتاب را باز کنید و پروژه CH11_AddressingCrossCuttingConcerns را انتخاب کنید
2️⃣ از منوی Project مسیر زیر را انتخاب کنید:
Project | CH11_AddressingCrossCuttingConcerns | Properties
3️⃣ در صفحه Properties پروژه، گزینه Code Analysis را انتخاب کنید، همانطور که در تصویر زیر نشان داده شده است ✅
همانطور که در تصویر قبلی مشاهده میکنید، اگر ببینید که پکیج Analyzer پیشنهادی نصب نشده است، روی Install کلیک کنید تا نصب شود.
پس از نصب، شماره نسخه در Installed Version Box نمایش داده میشود. برای من، نسخه 2.9.6 است.
به طور پیشفرض، Active Rules روی Microsoft Managed Recommended Rules تنظیم شده است.
محل این Ruleset، همانطور که در توضیحات نشان داده شده، به صورت زیر است:
C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Team Tools\Static Analysis Tools\Rule Sets\MinimumRecommendedRules.ruleset
فایل را باز کنید. این فایل به صورت یک پنجره ابزار در Visual Studio باز خواهد شد، همانطور که در تصویر زیر مشاهده میکنید. ✅
همانطور که در تصویر قبلی مشاهده میکنید، میتوانید قوانین (Rules) را انتخاب یا لغو انتخاب کنید. وقتی پنجره را بستید، از شما خواسته میشود تغییرات را ذخیره کنید.
برای اجرای Code Analysis، مسیر زیر را دنبال کنید:
Analyze and Code Cleanup | Code Analysis
برای مشاهده نتایج، باید پنجره Error List باز باشد. میتوانید آن را از منوی View باز کنید.
پس از اجرای تحلیل کد، لیستی از خطاها، هشدارها و پیامها مشاهده خواهید کرد. میتوانید هر یک از آنها را بررسی و اصلاح کنید تا کیفیت کلی نرمافزار خود را بهبود دهید.
نمونهای از این نتایج را میتوان در تصویر زیر مشاهده کرد ✅
همانطور که در تصویر قبلی مشاهده میکنید، پروژه CH10_AddressingCrossCuttingConcerns دارای ۳۲ هشدار و ۱۳ پیام است. اگر روی این هشدارها و پیامها کار کنیم، میتوانیم تعداد آنها را به ۰ هشدار و ۰ پیام کاهش دهیم.
حالا که دیدید چگونه با استفاده از Code Metrics میتوان میزان نگهداریپذیری نرمافزار را سنجید و با تحلیل کد، نقاط قابل بهبود را پیدا کرد، وقت آن است که به Quick Actions نگاهی بیندازیم. ⚡
استفاده از Quick Actions 🛠️💡
یکی دیگر از ابزارهای مفیدی که من دوست دارم استفاده کنم، ابزار Quick Action است. این ابزار معمولاً به صورت پیچگوشتی 🪛، لامپ 💡 یا لامپ خطا 💡⚠️ در کنار یک خط کد ظاهر میشود.
با Quick Actions میتوانید با یک دستور:
- کد تولید کنید
- کد را Refactor کنید
- هشدارها را Suppress کنید
- اصلاحات کد (Code Fix) انجام دهید
- و using statements اضافه کنید
از آنجا که پروژه CH10_AddressingCrossCuttingConcerns دارای ۳۲ هشدار و ۱۳ پیام بود، میتوانیم از این پروژه برای مشاهده عملکرد Quick Actions استفاده کنیم.
به تصویر زیر نگاه کنید تا عملکرد Quick Actions را ببینید ✅
با نگاه به تصویر قبلی، میبینیم لامپ 💡 روی خط ۱۰ ظاهر شده است.
اگر روی لامپ کلیک کنیم، منوی زیر ظاهر میشود ✅
اگر روی گزینه Add readonly modifier کلیک کنیم، Access Modifier مربوط به readonly
بعد از private قرار میگیرد.
خودتان با استفاده از Quick Actions کد را تغییر دهید. وقتی با آن کار کنید، روند کار نسبتاً ساده است. پس از تمرین با Quick Actions، به سراغ ابزار JetBrains dotTrace Profiler میرویم. ⚡
استفاده از پروفایلر JetBrains dotTrace 🖥️📊
پروفایلر JetBrains dotTrace بخشی از JetBrains ReSharper Ultimate است. از آنجا که در این فصل به هر دو ابزار نگاه میکنیم، توصیه میکنم قبل از ادامه، JetBrains ReSharper Ultimate را دانلود و نصب کنید.
JetBrains نسخه Trial هم ارائه میدهد، در صورتی که نسخه اصلی را ندارید. نسخههایی برای Windows، macOS و Linux موجود است.
پروفایلر dotTrace با Mono، .NET Framework و .NET Core کار میکند. تمام انواع برنامهها توسط این پروفایلر پشتیبانی میشوند و میتوانید از آن برای تحلیل و یافتن مشکلات Performance در کد استفاده کنید. این ابزار به شما کمک میکند مشکلاتی مانند:
- مصرف ۱۰۰٪ CPU
- استفاده کامل از I/O دیسک
- پر شدن حافظه
- بروز Overflow Exception
و بسیاری مشکلات دیگر را شناسایی کنید.
بسیاری از برنامهها درخواستهای HTTP ارسال میکنند. پروفایلر تحلیل میکند که چگونه برنامه این درخواستها را پردازش میکند، و همین کار را برای SQL Queries روی پایگاه داده نیز انجام میدهد. همچنین میتوانید Static Methods و Unit Tests را پروفایل کنید و نتایج را داخل Visual Studio مشاهده کنید. نسخهای Standalone نیز موجود است.
چهار گزینه اصلی پروفایلینگ 📈
- Sampling: اندازهگیری دقیق زمان فراخوانی متدها، مناسب برای شروع
- Tracing: جزئیات بیشتر پروفایلینگ، با overhead بالاتر (CPU و حافظه)
- Line-by-Line: تحلیل دقیق خط به خط، با overhead بیشتر
- Timeline: مشابه Sampling، اما Events برنامه را در طول زمان جمعآوری میکند
بین این چهار گزینه، هیچ مشکلی وجود ندارد که قابل شناسایی و رفع نباشد.
گزینههای پیشرفته پروفایلینگ شامل:
- Real-time Performance Counters: اندازهگیری زمان بین ورود و خروج متد
- Thread Time: زمان اجرای Thread
- Real-time CPU Instructions: زمان دقیق ورود و خروج متد بر اساس CPU register
- Thread Cycle Time: زمان چرخه Thread
پروفایل کردن برنامهها
پروفایلر میتواند به برنامهها و پردازههای در حال اجرای .NET Framework 4.0 یا بالاتر و .NET Core 3.0 یا بالاتر متصل شود، برنامههای محلی و راه دور را پروفایل کند.
این شامل موارد زیر است:
- برنامههای Standalone
- برنامههای .NET Core
- وب اپلیکیشنهای میزبان IIS و IIS Express
- سرویسهای .NET Windows Services و WCF
- برنامههای Windows Store و UWP
- هر پردازه .NET که پس از شروع جلسه پروفایلینگ اجرا شود
- برنامههای Desktop یا Console بر پایه Mono
- Unity Editor یا برنامههای مستقل Unity
دسترسی به پروفایلر در Visual Studio 2019
از منوی Extensions | ReSharper | Profile | Show Performance Profiler استفاده کنید.
در تصویر زیر میبینید که هنوز هیچ پروفایلی اجرا نشده است. پروژه انتخابشده Basic CH3 است و نوع پروفایلینگ روی Timeline تنظیم شده است.
برای پروفایل کردن پروژه CH3 با Sampling، Timeline Dropdown را باز کرده و Sampling را انتخاب کنید، همانطور که در تصویر بعدی نشان داده شده است ✅
اگر بخواهید پروژه دیگری را پروفایل کنید، کافی است Drop-down لیست Project را باز کرده و پروژه موردنظر خود را انتخاب کنید.
پروژه ساخته (Build) میشود و پروفایلر شروع به کار میکند. سپس پروژه شما اجرا شده و خاموش میشود.
نتایج در dotTrace Profiling Application نمایش داده میشوند، همانطور که در تصویر زیر مشاهده میکنید ✅
همانطور که در تصویر قبلی مشاهده میکنید، اولین از چهار Thread نمایش داده شده است. این Thread مربوط به برنامه ما است. سایر Threadها برای فرآیندهای پشتیبان هستند که اجرای برنامه را ممکن میکنند، همراه با Finalizer Thread که مسئول خروج از برنامه و پاکسازی منابع سیستم است.
موارد منوی All Calls در سمت چپ شامل موارد زیر است:
- Thread Tree
- Call Tree
- Plain List
- Hot Spots
در حال حاضر، گزینه Thread Tree انتخاب شده است.
حال نگاهی به Call Tree گسترش یافته در تصویر بعدی میاندازیم ✅
پروفایلر Call Tree کامل کد شما را نشان میدهد، که شامل کد سیستم و کد خودتان است.
شما میتوانید درصد زمان صرفشده برای هر فراخوانی را مشاهده کنید. این امکان به شما میدهد تا متدهای طولانی را شناسایی کرده و آنها را بهبود دهید.
حال، به Plain List نگاه میکنیم. همانطور که در تصویر بعدی مشاهده میکنید، در Plain List View میتوانیم دادهها را بر اساس معیارهای زیر گروهبندی کنیم:
- None
- Class
- Namespace
- Assembly
معیارهای فوق را میتوانید در تصویر زیر مشاهده کنید ✅
وقتی روی یک مورد در لیست کلیک میکنید، میتوانید کد منبع کلاسی که متد در آن قرار دارد را مشاهده کنید.
این ویژگی مفید است، زیرا میتوانید ببینید مشکل در کجا قرار دارد و چه کاری باید انجام شود.
آخرین صفحه پروفایلینگ با Sampling که بررسی میکنیم، Hot Spots View است، همانطور که در تصویر زیر نشان داده شده است ✅
پروفایلر نشان میدهد که Main Thread، که نقطه شروع کد ما است، تنها ۴.۵۹٪ از زمان پردازش را به خود اختصاص میدهد.
اگر روی Root کلیک کنید، مشاهده میکنید که ۱۸٪ کد مربوط به کد کاربر است و ۷۲٪ کد مربوط به کد سیستم میباشد، همانطور که در تصویر زیر نشان داده شده است ✅
ما تاکنون تنها سطحی از امکانات این ابزار پروفایلینگ را بررسی کردهایم. هنوز قابلیتهای بیشتری وجود دارد و توصیه میکنم خودتان آن را امتحان کنید. هدف اصلی این فصل، معرفی ابزارهای موجود برای بهبود کد شماست.
برای اطلاعات بیشتر درباره نحوه استفاده از JetBrains dotTrace، میتوانید به منابع آموزشی آنلاین آنها مراجعه کنید:
JetBrains dotTrace Documentation 📚
استفاده از JetBrains ReSharper 🛠️✨
در این بخش، میبینیم که چگونه JetBrains ReSharper میتواند به بهبود کد شما کمک کند.
ReSharper یک ابزار گسترده است و همانند پروفایلر که بخشی از نسخه Ultimate آن است، ما تنها به بررسی ابتدایی امکانات آن میپردازیم. هدف این است که با قابلیتهای ابزار آشنا شوید و بدانید چگونه میتواند تجربه کدنویسی شما در Visual Studio را بهبود دهد.
چند مزیت استفاده از ReSharper
- با ReSharper میتوانید کیفیت کد خود را تحلیل کنید ✅
- گزینههایی برای بهبود کد، حذف Code Smells و رفع مشکلات کدنویسی ارائه میدهد
- با سیستم ناوبری (Navigation System) میتوانید کل پروژه را پیمایش کرده و به هر موردی که نیاز دارید، بروید. امکانات جانبی شامل IntelliSense پیشرفته، بازآرایی کد و موارد دیگر است
- قابلیت Refactoring با ReSharper میتواند محلی یا در سطح کل راهحل (Solution-wide) باشد
- میتوانید کد منبع تولید کنید، مانند کلاسهای پایه و فوقکلاسها و متدهای Inline
- کد میتواند مطابق با سیاستهای کدنویسی شرکت پاکسازی شود، برای مثال حذف Imports غیرضروری و دیگر کدهای بلااستفاده
برای دسترسی به منوی ReSharper، از منوی Extensions در Visual Studio 2019 استفاده کنید.
وقتی در Code Editor هستید، با راستکلیک روی کد، منوی Context باز میشود و آیتمهای مرتبط نمایش داده میشوند.
آیتم منوی ReSharper در Context Menu، Refactor This… است، همانطور که در تصویر زیر نشان داده شده ✅
حالا، از منوی Visual Studio 2019 مسیر زیر را اجرا کنید:
Extensions | ReSharper | Inspect | Code Issues in Solution
ReSharper پروژه را پردازش کرده و سپس پنجره Inspection Results را نمایش میدهد، همانطور که در تصویر زیر مشاهده میکنید ✅
همانطور که در تصویر قبلی مشاهده میکنید، ReSharper تعداد ۵۲۷ مشکل را در کد ما پیدا کرده است، که ۴۳۶ مورد از آنها نمایش داده شدهاند. این مشکلات شامل موارد زیر میشوند:
- تمرینهای معمول و بهبود کد
- هشدارهای کامپایلر
- نقض محدودیتها
- فرصتهای استفاده بهتر از زبان
- مشکلات احتمالی کیفیت کد
- تکرارهای غیرضروری در کد
- تکرار در اعلان نمادها (Symbol Declarations)
- مشکلات املایی
- و سبک سینتکس (Syntax Style)
اگر بخش Compiler Warnings را باز کنیم، سه مشکل مشاهده میکنیم:
- فیلد
_name
هیچگاه مقداردهی نمیشود. - متغیر محلی
nre
هرگز استفاده نمیشود. - این متد async فاقد await است و به صورت همزمان (Synchronous) اجرا میشود. از await برای فراخوانیهای غیرمسدودکننده API یا
await TaskEx.Run(...)
برای کارهای CPU-bound در Thread پسزمینه استفاده کنید.
این مشکلات شامل اعلان متغیرهایی است که مقداردهی یا استفاده نمیشوند و همچنین متد async که فاقد await است و به صورت همزمان اجرا میشود.
اگر روی اولین هشدار کلیک کنید، به خط کدی میروید که مقداردهی نشده است. با نگاه به کلاس، میبینیم که رشته (string
) اعلان شده و استفاده میشود، اما هرگز مقداردهی نشده است. از آنجا که بررسی میکنیم آیا رشته string.Empty
است، میتوانیم آن را مقداردهی کنیم:
private string _name = string.Empty;
چون متغیر _name
هنوز هایلایت است، میتوانیم ماوس را روی آن ببریم و ببینیم مشکل چیست. Quick Action به ما میگوید که میتوان _name
را readonly کرد. پس آن را اضافه میکنیم:
private readonly string _name = string.Empty;
اگر روی دکمه Refresh کلیک کنیم، میبینیم که تعداد مشکلات یافتشده اکنون ۵۲۶ است. اما ما دو مشکل را رفع کردیم. پس چرا تعداد ۵۲۵ نیست؟ دلیل این است که دومین مشکلی که رفع شد، توسط ReSharper شناسایی نشده بود، بلکه یک بهبود بود که توسط Visual Studio Quick Actions پیشنهاد شد. بنابراین ReSharper تعداد صحیح مشکلات شناساییشده را نشان میدهد. ✅
حالا به مسئله احتمالی کیفیت کد (Potential Code Quality Issue) برای کلاس LooseCouplingB نگاه میکنیم. ReSharper گزارش میدهد که امکان بروز System.NullReferenceException در این متد وجود دارد:
public LooseCouplingB()
{
LooseCouplingA lca = new LooseCouplingA();
lca = null;
Debug.WriteLine($"Name is {lca.Name}");
}
همانطور که میبینیم، System.NullReferenceException در کد قابل مشاهده است.
برای بررسی کلاس LooseCouplingA و تعیین اینکه کدام اعضا باید مقداردهی شوند، میبینیم عضو _name
باید مقداردهی شود:
public string Name
{
get => _name.Equals(string.Empty) ? StringIsEmpty : _name;
set
{
if (value.Equals(string.Empty))
Debug.WriteLine("Exception: String length must be greater than zero.");
}
}
با توجه به اینکه _name
برای خالی بودن بررسی میشود، در واقع باید _name
به string.Empty
مقداردهی شود. پس کانستراکتور اصلاحشده LooseCouplingB به صورت زیر میشود:
public LooseCouplingB()
{
var lca = new LooseCouplingA
{
Name = string.Empty
};
Debug.WriteLine($"Name is {lca.Name}");
}
اگر پنجره Inspection Results را Refresh کنیم، میبینیم تعداد مشکلات ۵ کاهش یافته است، زیرا علاوه بر مقداردهی صحیح Property، از فرصت استفاده از زبان برای سادهسازی Instantiation و Initialization نیز استفاده کردیم که توسط ReSharper شناسایی شد.
ReSharper همچنین میتواند Dependency Diagram تولید کند. برای تولید نمودار وابستگی پروژه، مسیر زیر را انتخاب کنید:
Extensions | ReSharper | Architecture | Show Project Dependency Diagram
این نمودار وابستگی پروژه را نمایش میدهد.
- کادر مشکی به نام CH06: Namespace
- کادرهای خاکستری/آبی با پیشوند CH06_ : پروژهها
همانطور که در تصویر زیر نشان داده شده است ✅
همانطور که در Project Dependency Diagram مربوط به Namespace CH06 مشاهده میکنید، یک وابستگی پروژه بین CH06_SpecFlow و CH06_SpecFlow.Implementation وجود دارد.
به همین ترتیب، میتوانید با استفاده از ReSharper، Type Dependency Diagrams نیز تولید کنید. مسیر زیر را انتخاب کنید:
Extensions | ReSharper | Architecture | Type Dependencies Diagram
اگر نمودار را برای ConcreteClass در پروژه CH10_AddressingCrossCuttingConcerns تولید کنیم، نمودار ساخته میشود، اما در ابتدا تنها کلاس ConcreteComponent نمایش داده خواهد شد.
روی کادر ConcreteComponent در نمودار راستکلیک کرده و Add All Referenced Types را انتخاب کنید.
با این کار، کلاس ExceptionAttribute و اینترفیس IComponent اضافه میشوند.
سپس روی کلاس ExceptionAttribute راستکلیک کرده و دوباره Add All Referenced Types را انتخاب کنید تا به نتیجه نهایی برسید، همانطور که در تصویر زیر نشان داده شده است ✅
نکتهی واقعاً فوقالعاده در مورد این ابزار این است که میتوانید عناصر نمودار را بر اساس Namespace مرتب کنید.
این قابلیت برای راهحلهای بزرگ با چندین پروژه حجیم و Namespaceهای تو در تو بسیار مفید است.
اگرچه امکان راستکلیک روی کد و رفتن به Item Declaration خوب است، اما هیچ چیزی جای مشاهده بصری ساختار پروژه را نمیگیرد. همین ویژگی باعث میشود این ابزار واقعاً کاربردی باشد.
در تصویر زیر، نمونهای از Typed Dependencies Diagram را میبینید که بر اساس Namespaceها سازماندهی شده است ✅
بارها برای من پیش آمده که واقعاً به چنین نموداری در کار روزمره نیاز داشتم.
این نمودار، مستندسازی فنی است که به توسعهدهندگان کمک میکند در یک راهحل پیچیده مسیر خود را پیدا کنند. آنها میتوانند ببینند کدام Namespaceها در دسترس هستند و همه چیز چگونه به هم متصل شده است.
این اطلاعات به توسعهدهندگان دانش درست میدهد تا بدانند هنگام توسعه، کلاسها، Enumها و Interfaceهای جدید را کجا قرار دهند، و همچنین بدانند هنگام نگهداری کد، اشیاء را از کجا پیدا کنند.
این نمودار همچنین برای یافتن Namespaceها، Interfaceها و نامهای اشیاء تکراری نیز مفید است. ✅
نگاهی به Coverage 🔍
مراحل زیر را دنبال کنید:
- از منوی Extensions | ReSharper | Cover | Cover Application استفاده کنید.
- Dialog Configuration Coverage نمایش داده میشود و گزینه پیشفرض Standalone انتخاب شده است.
- فایل اجرایی (Executable) خود را انتخاب کنید.
- میتوانید یک برنامه .NET از پوشه bin انتخاب کنید.
- تصویر زیر Coverage Configuration Dialog را نشان میدهد ✅
۶. روی دکمه Run کلیک کنید تا برنامه اجرا شود و دادههای پروفایلینگ جمعآوری شوند.
پس از آن، ReSharper دیالوگ زیر را نمایش خواهد داد:
برنامه اجرا خواهد شد. در حین اجرای برنامه، پروفایلر پوشش کد در حال جمعآوری دادهها خواهد بود.
فایلی که انتخاب کردهایم یک برنامه کنسولی است که دادههای زیر را نمایش میدهد:
۷. روی پنجره کنسول کلیک کنید و سپس هر کلیدی را فشار دهید تا از برنامه خارج شوید.
پنجره پوشش (Coverage) بسته خواهد شد و فرآیند ذخیرهسازی دادهها آغاز میشود.
در نهایت، پنجره Coverage Results Browser نمایش داده خواهد شد، همانطور که در این تصویر میبینید:
این پنجره شامل اطلاعات بسیار مفیدی است. این ابزار با نشانگرهای رنگی به شما کمک میکند:
- کدهایی که اجرا نشدهاند با رنگ قرمز مشخص میشوند.
- کدهای اجرا شده با رنگ سبز نمایش داده میشوند.
با استفاده از این اطلاعات میتوانید تشخیص دهید که کد قرمز شده ممکن است:
- کد مرده (Dead Code) باشد و بتوان آن را حذف کرد،
- به دلیل مسیر اجرای برنامه فراخوانی نشده باشد اما همچنان مورد نیاز باشد،
- برای اهداف تست موقتاً کامنت شده باشد،
- یا به دلیل اشتباه برنامهنویس یا شرط اشتباه، اصلاً فراخوانی نشده باشد.
برای رفتن به آیتم موردنظر کافی است دو بار روی آن کلیک کنید تا مستقیماً به همان قطعه کد منتقل شوید.
در مثال ما، کلاس Program تنها 33٪ پوشش کد داشته است. با دوبار کلیک روی Program، خروجی زیر را مشاهده میکنیم:
static void Main(string[] args)
{
LoggingServices.DefaultBackend = new ConsoleLoggingBackend();
AuditServices.RecordPublished += AuditServices_RecordPublished;
DecoratorPatternExample();
//ProxyPatternExample();
//SecurityExample();
//ExceptionHandlingAttributeExample();
//SuccessfulMethod();
//FailedMethod();
Console.ReadKey();
}
همانطور که میبینید، دلیل پوشش پایین این است که چندین فراخوانی کد برای تست موقتاً کامنت شدهاند. در این حالت میتوانیم:
- کد را به همین صورت باقی بگذاریم (که ما در این مثال همین کار را میکنیم)،
- یا کدهای مرده را حذف کنیم،
- یا با برداشتن کامنتها آنها را دوباره فعال کنیم.
حال که با ReSharper و ابزارهای کمککننده برای نوشتن کدهای تمیز و باکیفیت در #C آشنا شدید، نوبت به ابزار بعدی میرسد: Telerik JustDecompile.
من بارها از این ابزار استفاده کردهام، برای مثال:
- رفع باگ در کتابخانههای شخص ثالث،
- بازیابی سورسکدهای مهم پروژههای از دسترفته،
- بررسی قدرت Obfuscation اسمبلیها،
- و حتی برای یادگیری و تحلیل کدهای موجود.
این ابزار را به شدت توصیه میکنم، چون در طول سالها ارزش خود را بارها ثابت کرده است.
موتور Decompile این ابزار متنباز است و میتوانید سورسکد آن را از این آدرس دریافت کنید:
https://github.com/telerik/justdecompileengine
همچنین نصبکننده ویندوز را میتوانید از وبسایت Telerik دریافت کنید:
https://www.telerik.com/products/decompiler.aspx
این ابزار هم به صورت برنامه مستقل و هم به صورت افزونه Visual Studio موجود است.
با آن میتوانید:
- پروژههای VB.NET یا #C را از اسمبلیها ایجاد کنید،
- منابع (Resources) را از اسمبلیهای Decompile شده استخراج و ذخیره کنید.
مراحل نصب و اجرا:
- Telerik JustDecompile را دانلود و نصب کنید.
- در حین نصب ممکن است ابزارهای اضافی پیشنهاد شوند، که در صورت تمایل میتوانید آنها را غیرفعال کنید.
- برنامه مستقل JustDecompile را اجرا کنید.
- یک اسمبلی .NET پیدا کرده و آن را به پنل سمت چپ برنامه بکشید.
- برنامه کد را Decompile کرده و ساختار درختی کد را در سمت چپ نمایش میدهد.
- با انتخاب هر آیتم در سمت چپ، کد آن در سمت راست نمایش داده میشود، همانطور که در تصویر زیر میبینید:
حالا، همانطور که میبینید، فرآیند دیکامپایل (decompilation) سریع است و در بیشتر موارد، نتیجهی خوبی برای دیکامپایل اسمبلی ما ارائه میدهد. البته این فرآیند بینقص نیست، اما معمولاً کار راه میاندازد. مراحل زیر را انجام دهید:
- در منوی کشویی سمت راست گزینهی Plugins، گزینهی C# را انتخاب کنید.
- سپس روی Tools | Create Project کلیک کنید.
- گاهی از شما خواسته میشود نسخهی .NET هدف را انتخاب کنید؛ اما همیشه این درخواست نمایش داده نمیشود.
- بعد از آن، از شما پرسیده میشود پروژه را کجا ذخیره کنید.
- پروژه در همان مسیر ذخیره خواهد شد.
بعد از این کار، شما میتوانید پروژه را در Visual Studio باز کرده و روی آن کار کنید. اگر با مشکلی مواجه شدید، Telerik خطاهای موجود در کد را ثبت کرده و یک ایمیل در اختیارتان میگذارد. شما میتوانید هر مشکلی را که با آن روبهرو شدید از طریق ایمیل برای آنها ارسال کنید. تیم پشتیبانی آنها معمولاً در پاسخدهی و رفع مشکلات خوب عمل میکند.
خب، حالا مرور این فصل به پایان رسید. بیایید به طور خلاصه مرور کنیم که چه آموختیم:
-
در این فصل دیدید که شاخصهای کیفی کد (Code Metrics) چه اطلاعات مفیدی ارائه میدهند و تولید آنها چقدر آسان است. این شاخصها شامل تعداد خطوط (حتی خطوط خالی) در مقابل خطوط اجرایی، پیچیدگی حلقهها (Cyclomatic Complexity)، سطح انسجام (Cohesion) و کوپلینگ (Coupling)، و میزان قابلیت نگهداری کد است. رنگهای مربوط به نیاز به بازآرایی کد به این صورت هستند: سبز (خوب)، زرد (نیاز به بازآرایی در حالت ایدئال)، قرمز (حتماً نیاز به بازآرایی دارد).
-
سپس یاد گرفتید که چطور بهراحتی میتوانید تحلیل ایستای کد انجام دهید و نتایج را ببینید. همچنین مشاهده و تغییر RuleSetها که مشخص میکنند چه مواردی تحلیل شوند یا نشوند، آموزش داده شد.
-
سپس Quick Actions را دیدیم که چطور میتوانند رفع اشکال، افزودن using statements و بازآرایی کد را تنها با یک دستور انجام دهند.
-
بعد به سراغ ابزار JetBrains dotTrace profiler رفتیم تا عملکرد برنامه را اندازهگیری کنیم، گلوگاهها را شناسایی کرده و متدهای پرمصرف را پیدا کنیم.
-
ابزار بعدی، JetBrains ReSharper بود که به ما امکان داد کد را از نظر مشکلات و بهبودهای احتمالی بررسی کنیم. چند مورد را شناسایی کردیم، تغییرات لازم را اعمال کردیم و دیدیم چطور بهراحتی میتوان با این ابزار کیفیت کد را بهبود داد.
-
سپس به ایجاد نمودارهای معماری برای وابستگیها و وابستگیهای نوع پرداختیم.
-
در نهایت، ابزار Telerik JustDecompile را بررسی کردیم؛ ابزاری بسیار کاربردی که میتواند اسمبلیها را دیکامپایل کرده و پروژههای C# یا VB.NET تولید کند. این ابزار زمانی مفید است که باگ پیدا میکنید یا برنامه نیاز به توسعه دارد ولی دیگر به کد منبع دسترسی ندارید.
در فصلهای بعدی، بیشتر روی کد و بازآرایی آن تمرکز خواهیم کرد. اما فعلاً، دانش خود را با پرسشهای زیر بیازمایید و از لینکهای بخش مطالعه بیشتر استفاده کنید:
سوالات
- Code Metrics چیست و چرا باید از آن استفاده کنیم؟
- شش مورد از شاخصهای اندازهگیری Code Metrics را نام ببرید.
- تحلیل کد (Code Analysis) چیست و چرا مفید است؟
- Quick Actions چیستند؟
- ابزار JetBrains dotTrace برای چه استفاده میشود؟
- ابزار JetBrains ReSharper برای چه استفاده میشود؟
- چرا از Telerik JustDecompile برای دیکامپایل اسمبلیها استفاده کنیم؟
مطالعه بیشتر
- مستندات رسمی مایکروسافت دربارهی Code Metrics:
https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-values?view=vs-2019 - مستندات رسمی مایکروسافت دربارهی Quick Actions:
https://docs.microsoft.com/en-us/visualstudio/ide/quick-actions?view=vs-2019 - ابزار JetBrains dotTrace profiler:
https://www.jetbrains.com/profiler/