PDA

View Full Version : راه استاندارد بررسی خطا در کلاسهای ساخته شده



HAIdle
چهارشنبه 17 مرداد 1386, 14:13 عصر
سلام بر همه دوستان
فرض کنید که ما کلاسی به شکل زیر ساخته ایم:


class AVRArray
{
private AVR[] _arr;
private int _len; // length of _arr
private int _number; // number of avilable avrs in the _arr

// constructor of this class
public AVRArray(int arrayLen)
{
Len = arrayLen;
_arr = new AVR[Len];
Number = 0;
}

public void add(AVR avr)
{
_arr[Number++] = avr;
}
}


ولی در تابع add باید دقت کنیم که number از len بیشتر نشود. حالا سوال من این است که راه استاندارد برای این بررسی چیست؟ مثلا آیا اگر آرایه به اندازه کافی جا ندارد و کاربر تقاضای افزودن می کند
ما باید یک جعبه پیام نشان دهیم یا راه دیگری استاندارد است؟ در واقع خود دات نت در کلاسهایش چگونه این بررسی را انجام میدهد؟

mehdi.mousavi
چهارشنبه 17 مرداد 1386, 14:59 عصر
سلام بر همه دوستان
فرض کنید که ما کلاسی به شکل زیر ساخته ایم:


class AVRArray
{
private AVR[] _arr;
private int _len; // length of _arr
private int _number; // number of avilable avrs in the _arr

// constructor of this class
public AVRArray(int arrayLen)
{
Len = arrayLen;
_arr = new AVR[Len];
Number = 0;
}

public void add(AVR avr)
{
_arr[Number++] = avr;
}
}
ولی در تابع add باید دقت کنیم که number از len بیشتر نشود. حالا سوال من این است که راه استاندارد برای این بررسی چیست؟ مثلا آیا اگر آرایه به اندازه کافی جا ندارد و کاربر تقاضای افزودن می کند
ما باید یک جعبه پیام نشان دهیم یا راه دیگری استاندارد است؟ در واقع خود دات نت در کلاسهایش چگونه این بررسی را انجام میدهد؟



سلام.
شما باید به دو مساله دقت کنید.

1. ایرادهایی که بر خلاف انتظار شما و هنگام تست برنامه در کد رخ میده.
2. ایرادهایی که بر اساس استفاده ناصحیح از کلاس توسط کاربر دیگری رخ میده.

مساله اول، در مورد Pre-Condition و Post-Condition ها صحبت میکنه. هنگامیکه شما تابعی می نویسید، باید PostCondition ها و PreCondition های تابع رو بصورت Comment در برنامه قرار بدین. PreCondition ها، شرائطی هستن که شما انتظار دارید هنگام اجرای تابع برقرار باشن. تابع Add زیر رو در نظر بگیرید:



private Int32 AddPositiveNumbers(Int32 a, Int32 b)
{
return a + b;
}

فرض کنید که تابع فوق رو فقط بعنوان یه تابع کمکی در برنامه من استفاده می کنم، و هیچکس به این متود دسترسی نخواهد داشت. همونطور که اسم تابع نشون میده، میخوام دو تا عدد مثبت رو این تابع با هم جمع کنه. پس دارم فرض میکنم که دو عدد مثبت به عنوان a و b به این تابع داده بشه. پس پیش شرط من (Precondition) برای اجرای این تابع مثبت بودن دو عدد a و b هستش. خوشبختانه نیازی نیست تا من این فرض رو بصورت Comment به برنامه اضافه کنم. بلکه با اضافه کردن کد زیر به اولین خط تابع، این عمل رو انجام میدم:



private Int32 AddPositiveNumbers(Int32 a, Int32 b)
{
Debug.Assert(a >= 0 && b >= 0);
return a + b;
}

به این ترتیب هر وقت تو حالت Debug اعداد پاس شده به این تابع منفی باشه، Compiler یه Error میده و اجرا روی این خط متوقف میشه تا من ببینم کجا اشتباه کردم و عدد منفی به تابع فرستادم.

Post-Condition ها در واقع به شرایط اجرایی برنامه پس از اجرای یه تابع اشاره میکنه. به همین دلیل هستش که توضیحات باید بعنوان Comment در بالای یه تابع نوشته بشه:



/// <summary>
/// Adds to positive integer values.
/// </summary>
/// <param name="a">The firt integer value.</param>
/// <param name="b">The second integer value</param>
/// <returns>Gets the summation of two positive integer values passed a the parameter.</returns>
private Int32 AddPositiveNumbers(Int32 a, Int32 b)
{
Debug.Assert(a >= 0 && b >= 0);
return a + b;
}

به این ترتیب خواننده هم PreCondition های تابع رو میدونه، هم PostCondition های اونو.

اما در مورد شماره 2، یعنی ایرادهایی که بر اساس استفاده ناصحیح از کلاس توسط کاربر دیگری رخ میده. برنامه ای رو در نظر بگیرید که در اون کاربر میتونه دو عدد رو تعیین کنید و جمع اون دو عدد رو ببینه. ما رو صفحه نوشتیم که "لطفا عدد مثبت وارد کنید" اما کاربر برای اینکه کنجکاوی خودش رو ارضاء کنه، دو عدد منفی وارد میکنه. ما هم بدون اینکه Entry کاربر رو چک کنیم، اونها رو به تابع فوق پاس میکنیم. در چنین شرایطی، کد فوق رو باید به صورت زیر بنویسیم:



/// <summary>
/// Adds to positive integer values.
/// </summary>
/// <param name="a">The firt integer value.</param>
/// <param name="b">The second integer value</param>
/// <returns>Gets the summation of two positive integer values passed a the parameter.</returns>
public Int32 AddPositiveNumbers(Int32 a, Int32 b)
{
if (!(a >= 0 && b >= 0))
throw new Exception("enter two positive numbers");

return a + b;
}همونطوریکه می بینید اینجا دیگه مساله فرق کرده. ما پیش شرطهامون رو بصورت Exception داریم throw میکنیم، اگر برقرار نباشن. امیدوارم جواب سوالتون رو گرفته باشید.

HAIdle
چهارشنبه 17 مرداد 1386, 21:38 عصر
بسیار ممنون از جواب خوب شما
ولی چند تا چیز برام مبهم است:
1) debug.assert در چه namespace است؟ چون در حالت عادی و بدون اضافه کردن

namespace دات نت آنرا تشخیص نمی دهد.
2) ایرادهایی که در اثر استفاده نادرست کاربر ایجاد می شوند را متوجه شدم ولی ایرادهایی که بر خلاف انتظار ماست را نه!

دقیقا اشکالات زیر را در رابطه با آن دارم:
2-1) نوشتن preconditions و postconditions بصورت commentچه مشکلی را

حل می کند؟
2-2) اگر ما بدانیم که precondition و postcondition ها چیستند پس این اشکالات اشکالاتی

نیست که ما از آنها اطلاع نداشته باشیم و ناخواسته باشد.

mehdi.mousavi
چهارشنبه 17 مرداد 1386, 22:29 عصر
بسیار ممنون از جواب خوب شما
ولی چند تا چیز برام مبهم است:
1) debug.assert در چه namespace است؟ چون در حالت عادی و بدون اضافه کردن

namespace دات نت آنرا تشخیص نمی دهد.
2) ایرادهایی که در اثر استفاده نادرست کاربر ایجاد می شوند را متوجه شدم ولی ایرادهایی که بر خلاف انتظار ماست را نه!

دقیقا اشکالات زیر را در رابطه با آن دارم:
2-1) نوشتن preconditions و postconditions بصورت commentچه مشکلی را

حل می کند؟
2-2) اگر ما بدانیم که precondition و postcondition ها چیستند پس این اشکالات اشکالاتی

نیست که ما از آنها اطلاع نداشته باشیم و ناخواسته باشد.

خواهش می کنم.

1. کلاس Debug در System.Diagnostics قرار داره.

2. ببینید، کلیه شروطی که به عنوان فرضیات برنامه نویس هنگام اجرای یک تابع باید برقرار باشن، رو بهش Pre-Condition میگیم. قدیم چون ما هیچ ابزاری برای کنترل این پیش شرطها نداشتیم، میگفتن که باید اونها رو بصورت Comment بالای تابع بنوسیم تا یادمون باشه که مثلا فلان تابع رو فقط در فلان شرائط باید فراخوانی کنیم. بدین ترتیب برنامه نویس با خوندن اون comment ها می دونست که باید یه تابع رو در چه وضعیتهایی فراخوانی کنه. اما خیلی ها اون comment ها را نمیخونن، یا توجهی بهش ندارن. برای همین ابزارهایی برای کنترل این مساله بوجود اومد تا بشه این پیش شرطها رو در تابع نوشت (و دیگه اونها رو بصورت comment ننوشت) تا هرگاه اونها برقرار نبودن، سیستم بهمون اخطار بده و ما متوجه بشیم که داریم تابع رو در وضعیتی غیر از اون وضعیتی که موقع نوشتنش در نظر گرفته بودیم، فراخوانی میکنیم. یکی از این کلاسها در C#، کلاس Debug هست. متود Assert روی این کلاس این کار رو انجام میده. البته کلاس Debug همونطوری که از اسمش هم بر میاد، فقط در حالتی اجرا میشه که نسخه Debug برنامه رو بسازین، نه Release رو.

پرسیدین اگه ما بدونیم که PreCondition ها و PostCondition ها چین، خوب دیگه مشکلی که ما اطلاع نداشته باشیم پیش نمیاد. قبل از هر چیز باید خدمتتون عرض کنم که ما میدونیم Post/Pre Condition های یه تابع رو (چون خودمون داریم مینویسیمش)، اما به مرور زمان و با بزرگ شدن پروژه فراموش خواهیم کرد که فلان شرط هنگام فراخوانی فلان تابع باید برقرار باشه.

کد زیر رو در نظر بگیرید:



private void MyFunction()
{
String s = "test";
Char ch = GetFirstCharOfString(s);
}

private Char GetFirstCharOfString(String str)
{
return str[0];
}

وقتی من GetFirstCharOfString رو مینوشتم، پیش خودم گفتم که هروقت بخوام این تابع رو Call کنم باید حواسم باشه که بهشString ای ندم که null باشه، والا برنامه به مشکل میخوره. (پس این شد PreCondition این تابع). بعد جاهای دیگه برنامه اومدم این تابع رو فراخوانی کردم. (مثلا MyFunction). بعد از یه هفته اومدم یه تابع جدید اضافه کردم به کد، و نیاز داشتم تا همون GetFirstCharOfString رو توش فراخوانی کنم:



Char ch = GetFirstCharOfString(s);
اما حواسم نبوده و s، اینجا null به این تابع Pass میشه. وقتی برنامه رو اجرا کنم، این Exception تولید میشه:


Object reference not set to an instance of an object.


و من نمیدونم مشکل کجا بوده! حالا مجبورم خط به خط برنامه رو دنبال کنم تا ببینم این ایراد کجا بوده. درصورتیکه اگر Pre-Condition ها رو نوشته بودم:



private Char GetFirstCharOfString(String str)
{
Debug.Assert(!String.IsNullOrEmpty(str));
return str[0];
}
دیگه این مشل پیش نمیومد و اجرا رو خط Assert متوقف می شد. بعد Call Stack رو نگاه میکردم و میدیدم که این تابع از فلان نقطه Call شده و متوجه میشدم که s رو دارم null یا Empty رد می کنم.

بازم اگه توضیح خواستید در خدمتم.

HAIdle
پنج شنبه 18 مرداد 1386, 14:03 عصر
از پاسخ کامل شما بسیار ممنونم