آموزش دیباگ کردن در حالت نرمال در سی شارپ

Ratings
(0)

 

یکی از فرمان هایی که در سراسر این کتاب، از آن استفاده کرده ایم، تابع WriteLine() است، که متن ها را در کنسول بعنوان خروجی ایجاد می کند. چونکه می خواهیم اپلیکیشن ها را ایجاد کنیم، این تابع برای دادن بازخورد اضافی در مورد عملیات ها مفید است:

WriteLine("MyFunc() Function is about to be called.");
MyFunc("Do something.");
WriteLine("MyFunc() Function execution completed.");

این قطعه کد نشان می دهد که چگونه می توانیم اطلاعات اضافی را مربوط به یک تابع به نام MyFunc() دریافت کنیم. همه ی اینها بسیار خوب است اما این کار می تواند باعث شود خروجی کنسول ما کمی به هم ریخته شود. همچنین وقتی که داریم دیگر انواع اپلیکیشن ها را ایجاد می کنیم، این کار باعث می شود کدها درهم و برهم شود. مانند اپلیکیشن های دسکتاپ، که در آن برای خروجی اطلاعات، کنسول وجود ندارد. 

بعنوان یک جایگزین، می توانیم متن ها را در یک موقعیت جداگانه یعنی در پنجره ی Output در IDE بعنوان خروجی نشان دهیم. در فصل 2 که پنجره ی Error List را شرح می دهد، اشاره کرده ایم که دیگر پنجره ها هم می توانند در همین مکان نمایش داده شوند. یکی از اینها، پنجره ی Output است که می تواند برای دیباگ کردن بسیار مفید باشد. برای نمایش دادن این پنجره، از منوها به آدرس View ➪ Output بروید. این پنجره، اطلاعاتی را مربوط به کامپایل کردن و اجرای کدها ارائه می دهد؛ که شامل خطاهایی که در حین کامپایل شدن رخ می دهد، می شود. شما همچنین می توانید از این پنجره مانند آنچه در تصویر 7.1 نشان داده شده است، استفاده کنید. تا اطلاعات تشخیصی شخصی را با نوشتن در آن به طور مستقیم، نمایش دهید. 


نکته: پنجره ی Output شامل یک منوی کشویی است که در آن می توانیم حالت های(mode) مختلف را انتخاب کنیم؛ که شامل Build و Build Order و Debug می شود. این حالت ها، به ترتیب اطلاعات کامپایل و زمان اجرا(runtime) را نمایش می دهند. در این بخش، وقتی که از عبارت "نوشتن در پنجره ی Output" سخن می گوییم، به معنی "نوشتن در نمای حالت دیباگ در پنجره ی Output" است. 

(تصویر 7.1)

تصویر دیباگ در سی شارپ

به طور جایگزین، ممکن است بخواهیم یک فایل ورود به سیستم(logging) ایجاد کنیم که وقتی اپلیکیشن ما اجرا می شود، اطلاعاتی در آن اضافه شود. تکنیک های انجام این کار، خیلی شبیه به تکنیک های نوشتن متن در پنجره ی Output است، اگرچه این فرایند نیاز به یک درک، از روش دسترسی به filesystem در اپلیکیشن های سی شارپ دارد. فعلاً این قابلیت را در اولویت پایین قرار دهید؛ زیرا بدون گیر افتادن در فنون file-access کارهای زیادی است که می توانیم انجام دهیم. 


نشان دادن اطلاعات دیباگ در خروجی

نوشتن متن در پنجره ی Output در زمان اجرای برنامه(runtime)، آسان است.  ما به سادگی فراخوانی های  WriteLine() را با فراخوانی مورد نیاز جایگزین می کنیم تا متن ها را در جایی که لازم داریم بنویسیم. برای انجام این کار، دو دستور وجود دارد که می توانیم از آن استفاده کنیم:

  • Debug.WriteLine()
  •  Trace.WriteLine()

این دستورات تقریباً به همان روش کار می کنند، اما با یک تفاوت کلیدی؛ دستور اول، تنها در build های(ساخت نتیجه های) دیباگ کار می کند. دستور دوم نیز برای build های release کار می کند. در حقیقت، دستور Debug.WriteLine() حتی در یک build (ساخت نتیجه ی) release نیز کامپایل نمی شود. بلکه تنها ناپدید می شود؛ که قطعا مزیت هایی دارد(یکی اینکه باعث می شود کدهای کامپایل شده کوچکتر شوند). 

نکته: هردوی متدهای Debug.WriteLine() و Trace.WriteLine() در داخل فضای نام System.Diagnostics قرار دارند. از دستور using static تنها می توانیم در کلاس های استاتیک استفاده کنیم، بعنوان مثال، دستور System.Console متد WriteLine() را اضافه می کند و از این رو نمی تواند با آن تابع های WriteLine() مورد استفاده قرار گیرد. این تابع ها، دقیقاً مانند تابع WriteLine() کار نمی کنند. آنها به جای اینکه به ما امکان دهند تا با استفاده از سینتکس {X} مقادیر متغیر را وارد کنیم، برای نشان دادن پیغام در خروجی، تنها با یک پارامتر رشته ای(string) کار می کنند. این یعنی اینکه ما باید از یک روش جایگزین برای اضافه کردن مقادیر متغیر در رشته ها استفاده کنیم؛ بعنوان مثال می توانیم از عملگر پیوند + استفاده کنیم. 

ما همچنین می توانیم (به صورت اختیاری) یک پارامتر string دوم را ارائه دهیم؛ که برای متن خروجی، یک دسته(category) را نمایش می دهد. این به ما امکان می دهد تا نگاه کنیم که کدام پیغامِ خروجی در پنجره ی Output نمایش داده شده است. که وقتی که پیغام های مشابهی از مکان های مختلفی در اپلیکیشن در خروجی قرار می گیرند مفید است. خروجی کلی این تابع ها، به صورت زیر است: 

<category>: <message>

بعنوان مثال، در دستور زیر، از  "MyFunc" بعنوان پارامتر اختیاری دسته(category) استفاده شده است:

Debug.WriteLine("Added 1 to i", "MyFunc");

نتیجه ی آن به صورت زیر خواهد بود: 

MyFunc: Added 1 to i

در مثال خودتان امتحان کنید بعدی، روش نشان دادن اطلاعات دیباگ در خروجی، به این روش، نشان داده شده است.  


خودتان امتحان کنید: نوشتن متن در پنجره ی Output

آدرس مثال: Ch07Ex01\Program.cs

1. یک اپلیکیشن جدید کنسول به نام Ch07Ex01 ایجاد کنید و آن را در آدرس C:\BeginningCSharp7\Chapter07 ذخیره کنید. 

2. کدهای زیر را در Program.cs وارد کنید:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using static System.Console;

namespace Ch07Ex01
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] testArray = { 4, 7, 4, 2, 7, 3, 7, 8, 3, 9, 1, 9 };
            int maxVal = Maxima(testArray, out int[] maxValIndices);
            WriteLine($"Maximum value {maxVal} found at element indices:");
            foreach (int index in maxValIndices)
            {
                WriteLine(index);
            }
            ReadKey();
        }
        static int Maxima(int[] integers, out int[] indices)
        {
            Debug.WriteLine("Maximum value search started.");
            indices = new int[1];
            int maxVal = integers[0];
            indices[0] = 0;
            int count = 1;
            Debug.WriteLine(string.Format(
               $"Maximum value initialized to {maxVal}, at element index 0."));
            for (int i = 1; i < integers.Length; i++)
            {
                Debug.WriteLine(string.Format(
                   $"Now looking at element at index {i}."));
                if (integers[i] > maxVal)
                {
                    maxVal = integers[i];
                    count = 1;
                    indices = new int[1];
                    indices[0] = i;
                    Debug.WriteLine(string.Format(
                         $"New maximum found. New value is {maxVal}, at " +
                         $"element index {i}."));
                }
                else
                {
                    if (integers[i] == maxVal)
                    {
                        count++;
                        int[] oldIndices = indices;
                        indices = new int[count];
                        oldIndices.CopyTo(indices, 0);
                        indices[count - 1] = i;
                        Debug.WriteLine(string.Format(
                           $"Duplicate maximum found at element index {i}."));
                    }
                }
            }
            Trace.WriteLine(string.Format(
               $"Maximum value {maxVal} found, with {count} occurrences."));
            Debug.WriteLine("Maximum value search completed.");
            return maxVal;
        }
    }
}

3. کدها را در حالت دیباگ(debug mode) اجرا کنید.  نتیجه در تصویر 7.2 نشان داده شده است. 

(تصویر 7.2)

نتیجه ی اجرای کد در سی شارپ4. به اجرای اپلیکیشن خاتمه دهید و محتوای پنجره ی Output را (در حالت دیباگ) بررسی کنید. یک نسخه ی کوتاه شده از output به صورت زیر است:

...
Maximum value search started.
Maximum value initialized to 4, at element index 0.
Now looking at element at index 1.
New maximum found. New value is 7, at element index 1.
Now looking at element at index 2.
Now looking at element at index 3.
Now looking at element at index 4.
Duplicate maximum found at element index 4.
Now looking at element at index 5.
Now looking at element at index 6.
Duplicate maximum found at element index 6.
Now looking at element at index 7.
New maximum found. New value is 8, at element index 7.
Now looking at element at index 8.
Now looking at element at index 9.
New maximum found. New value is 9, at element index 9.
Now looking at element at index 10.
Now looking at element at index 11.
Duplicate maximum found at element index 11.
Maximum value 9 found, with 2 occurrences.
Maximum value search completed.
The thread #### has exited with code 0 (0x0).

5. با استفاده از منوی کشویی که در نوار ابزار Standard قرار دارد، حالت Release را انتخاب کنید؛ این موضوع در تصویر 7.3 نشان داده شده است.

(تصویر 7.3)

انتخاب حالت release در سی شارپ

6. دوباره برنامه را اجرا کنید؛ اما این بار این بار در حالت release، این کار را انجام دهید. و دوباره پنجره ی Output را وقتی اجرای برنامه خاتمه می یابد، بررسی کنید. خروجی(کوتاه شده) به صورت زیر خواهد بود:

...
Maximum value 9 found, with 2 occurrences.
The thread #### has exited with code 0 (0x0).

این برنامه چگونه کار می کند؟ 

این اپلیکیشن، یک نسخه ی گسترش یافته از برنامه ای است که در فصل 6 با استفاده از یک تابع، مقدار ماکزیمم را در یک آرایه از اعداد صحیح، محاسبه می کرد. این نسخه همچنین یک آرایه از اندیس ها را برمی گرداند که در آن مقادیر ماکزیمم مشخص می شوند. به طوری که کدی که فراخوانی می شود می تواند این عناصر را دستکاری کند. 

 اول، یک دستور using در ابتدای این کد ظاهر می شود:

using System.Diagnostics;

استفاده از این دستور، باعث می شود دسترسی به تابع هایی که قبلا توضیح داده شدند، ساده تر شود؛ زیرا آنها در فضای نام System.Diagnostics قرار دارند. اگر از دستور using استفاده نکنیم، کدهایی مثل کد زیر:

Debug.WriteLine("BeginningC#");

طولانی تر می شوند و باید آنها را به صورت زیر بنویسیم:

System.Diagnostics.Debug.WriteLine("BeginningC#");

کد درون تابع  Main() به سادگی، یک آرایه از اعداد صحیح(integer) به نام testArray را مقداردهی اولیه می کند. 

 

این تابع، همچنین یک آرایه ی دیگر از اعداد صحیح، به نام maxValIndices را تعریف می کند که خروجی اندیس های Maxima()، یعنی تابعی که محاسبات را انجام می دهد، ذخیره می کند، سپس این تابع را فراخوانی می کند. 

 

هنگامی که این تابع return می کند، نتایج کدها در خروجی قرار می گیرد. 

 

تابع Maxima() کمی پیچیده تر است، اما کدهای آن خیلی ناآشنا نیست. 

 

جستجو در این آرایه به روش مشابهی در تابع MaxVal() در فصل 6 انجام شده است اما یک رکورد از اندیس های مقادیر ماکزیمم نگهداری شده است. 

 

به تابعی که از آن برای نگهداری این اندیس ها استفاده شده است، توجه کنید(به غیر از خطوطی که اطلاعات دیباگ را در خروجی نشان می دهند). 

 

 

 

به جای اینکه یک آرایه را برگردانیم(return کنیم) که برای ذخیره ی هر اندیس در آرایه ی منبع، به اندازه ی کافی بزرگ باشد،(به ابعاد مشابهی مانند آرایه ی منبع نیاز داریم)، تابع Maxima() یک آرایه را برمی گرداند که به اندازه ی کافی بزرگ است تا اندیس های پیدا شده را نگهداری کند.

 

 

تابع مذکور، این کار را با ایجاد پیوسته ی آرایه ها با سایزهای مختلف، همان طور که جستجو پیشرفت می کند، انجام می دهد.  

 

این کار ضروری است زیرا هنگامی که این آرایه ها ایجاد شدند، دیگر نمی توانند تغییر اندازه دهند. 

 

این جستجو، با فرض اینکه عنصر اول در آرایه ی منبع(که به صورت محلی integers نامیده می شود) مقدار ماکزیمم است و اینکه تنها یک مقدار ماکزیمم در این آرایه وجود دارد، مقدار دهی اولیه می شود. 

 

بنابراین، مقادیر می توانند برای maxVal (مقدار برگشتی این تابع و حداکثر مقدار یافت شده) و indices و آرایه ی پارامتری out که اندیس های مقادیر ماکزیمم پیدا شده را ذخیره می کند، تنظیم شوند. 

 

Values can therefore be
set for maxVal (the return value of the function and the maximum value found) and indices,

 

the out
parameter array that stores the indices of the maximum values found.

 

در خط 28، مقدار اولین عنصر در integers  در متغیر maxVal انتساب داده شده است و indices برابر با 0 شده است؛ که درواقع اندیس اولین عنصر از این آرایه می باشد. 

 

ما همچنین تعداد مقادیر ماکزیمم  پیدا شده را در یک متغیر به نام count ذخیره می کنیم؛ که به ما امکان می دهد تا آرایه ی indices را پیگیری کنیم. 

 

 

بدنه ی اصلی تابع Maxima یک حلقه(loop) است که روی مقادیر آرایه ی integers چرخه می زند. در خط 33، در حلقه ی مذکور، برای i از عدد 1 شروع کرده ایم، و مقدار 0 را از قلم انداخته ایم، زیرا (این مقدار) قبلاً پردازش شده است. 

 

در خط 37، هریک از مقادیر، با مقدار فعلی maxVal مقایسه می شوند و اگر maxVal بزرگتر باشد، نادیده گرفته می شود. 

 

اگر مقدار آرایه‌ی بررسی‌شده‌ی فعلی، بزرگتر از maxVal باشد، آنگاه maxVal و indices تغییر می کنند تا این (موضوع) را در کدها بازتاب دهند. 

 

در خط 49، اگر مقدار مذکور برابر با maxVal باشد، آنگاه متغیر count یک واحد افزایش می یابد و یک آرایه ی جدید برای indices جایگزین می شود. 

 

این آرایه ی جدید، در خط 53، یک واحد بزرگتر از آرایه ی قدیمی indices است و حاوی اندیس جدیدی می باشد. 

 

 

 

کدهای این بخش از توضیحات را در زیر مشاهده می کنید:



 

 

 

if (integers[i] == maxVal)
{
  count++;
  int[] oldIndices = indices;
  indices = new int[count];
  oldIndices.CopyTo(indices, 0);
  indices[count - 1] = i;
  Debug.WriteLine(string.Format($"Duplicate maximum found at element index {i}."));
}

در کدهای بالا، در خط شماره 4 مقدار قدیمی آرایه ی indices بعنوان پشتیبان در داخل  oldIndices قرار می گیرد، که یک آرایه ی صحیح(integer) محلی در این دستور if به حساب می آید. 

 

 

توجه کنید که مقادیر درون oldIndices، با استفاده از تابع <array>.CopyTo() در داخل آرایه ی جدید indices کپی می شود. 

 

این تابع به سادگی یک آرایه ی هدف را گرفته و از یک اندیس برای مشخص کردن اولین عنصری که کپی باید از آن انجام شود، استفاده می کند و همه ی مقادیر را در آرایه ی هدف الصاق(paste) می کند.  

 

در سراسر این کدها، متن های زیادی، با استفاده از تابع های Debug.WriteLine() و Trace.WriteLine() در خروجی نشان داده شده اند. 

 

 

در این تابع ها، از تابع  string.Format() استفاده شده است تا بتوانیم مقادیر متغیر را در رشته ها، مانند تابع WriteLine() اضافه کنیم. 

 

این روش، کمی کارآمدتر از استفاده از عملگر پیوند یا همان + است. 

 

هنگامی که این اپلیکیشن را در حالت دیباگ(debug mode) اجرا کنیم، یک گزارش کامل از مراحل انجام شده در حلقه را مشاهده خواهیم کرد، که نتیجه را به ما نشان می دهد. در حالت release، تنها نتایج محاسبات نمایش داده می شود؛ زیرا در این حالت، دستورات Debug.WriteLine() فراخوانی نمی شوند. 

 

 

 

 

 

 

  • بازدید: 76

نوشتن دیدگاه

لطفا نظرات خود را بیان کنید. به سوالات در سریع ترین زمان پاسخ داده خواهد شد.اما به نکات زیر توجه کنید:
1. سعی کنید نظرات شما مرتبط با مقاله ی مورد نظر باشد، در غیر این صورت پاسخ داده نخواهد شد.
2. سوالات خود را به صورت کوتاه بیان کنید و از پرسیدن چند سوال به طور همزمان خودداری کنید.
3. سوال خود را به طور واضح بیان کنید و از کلمات مبهم استفاده نکنید.

ارسال