Exception Handling و Debugging 🐛🔧
وقتشه یاد بگیریم چطور با خطاها مقابله کنیم!
Exception Handling: مدیریت خطاها 🛡️
هر برنامهای ممکنه با خطا مواجه بشه! Exception Handling به ما کمک میکنه این خطاها رو مدیریت کنیم تا برنامه کرش نکنه. فکر کنید Exception ها مثل سیستم ایمنی ماشین هستند - وقتی مشکلی پیش میاد، برنامه رو محافظت میکنند.
ساختار try-catch-finally
- try: کدی که ممکن است خطا داشته باشد
- catch: مدیریت خطاهای خاص
- finally: کدی که همیشه اجرا میشود (حتی در صورت خطا)
using System;
class Program
{
static void Main()
{
try
{
// کدی که ممکنه خطا داشته باشه
Console.Write("یک عدد وارد کنید: ");
string input = Console.ReadLine();
int number = int.Parse(input); // اگر متن وارد شده عدد نباشه، خطا میده
int result = 100 / number; // اگر عدد صفر باشه، خطا میده
Console.WriteLine($"نتیجه: {result}");
}
catch (FormatException ex)
{
// خطای تبدیل متن به عدد
Console.WriteLine("❌ خطا: لطفاً یک عدد معتبر وارد کنید!");
Console.WriteLine($"جزئیات: {ex.Message}");
}
catch (DivideByZeroException ex)
{
// خطای تقسیم بر صفر
Console.WriteLine("❌ خطا: نمیتوان بر صفر تقسیم کرد!");
}
catch (OverflowException ex)
{
// خطای سرریز عدد
Console.WriteLine("❌ خطا: عدد وارد شده خیلی بزرگ است!");
}
catch (Exception ex)
{
// سایر خطاها (این باید آخرین catch باشد)
Console.WriteLine($"❌ خطای غیرمنتظره: {ex.Message}");
}
finally
{
// این بخش همیشه اجرا میشود
Console.WriteLine("✅ پایان عملیات - منابع پاک شدند.");
}
}
}
انواع رایج Exception ها 📋
آشنایی با انواع مختلف Exception ها به شما کمک میکند بهتر آنها را مدیریت کنید:
خطاهای ورودی
FormatException- تبدیل نادرستArgumentException- پارامتر نامعتبرArgumentNullException- پارامتر null
خطاهای ریاضی
DivideByZeroException- تقسیم بر صفرOverflowException- سرریز عددArithmeticException- خطای ریاضی
خطاهای حافظه
NullReferenceException- دسترسی به nullIndexOutOfRangeException- ایندکس نامعتبرOutOfMemoryException- کمبود حافظه
خطاهای سیستم
FileNotFoundException- فایل پیدا نشدUnauthorizedAccessException- دسترسی غیرمجازTimeoutException- زمان انتظار تمام شد
using System;
using System.IO;
class FileManager
{
public static void ReadFile(string filePath)
{
try
{
string content = File.ReadAllText(filePath);
Console.WriteLine("محتوای فایل:");
Console.WriteLine(content);
}
catch (FileNotFoundException)
{
Console.WriteLine($"❌ فایل '{filePath}' پیدا نشد!");
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("❌ شما اجازه دسترسی به این فایل را ندارید!");
}
catch (DirectoryNotFoundException)
{
Console.WriteLine("❌ مسیر مشخص شده وجود ندارد!");
}
catch (IOException ex)
{
Console.WriteLine($"❌ خطا در خواندن فایل: {ex.Message}");
}
}
static void Main()
{
ReadFile("test.txt");
ReadFile("C:\\NonExistent\\file.txt");
}
}
Exception های سفارشی 🎯
گاهی نیاز داریم Exception های سفارشی بسازیم تا خطاهای خاص برنامهمان را بهتر مدیریت کنیم. این کار باعث میشود کد ما واضحتر و قابل فهمتر باشد.
چرا Exception سفارشی بسازیم؟
- ✅ وضوح بیشتر: نام Exception مشخص میکند چه مشکلی رخ داده
- ✅ مدیریت بهتر: میتوانیم هر نوع خطا را جداگانه مدیریت کنیم
- ✅ اطلاعات اضافی: میتوانیم ویژگیهای خاص اضافه کنیم
- ✅ حرفهایتر: کد ما استاندارد و قابل نگهداری میشود
using System;
// Exception سفارشی برای سن نامعتبر
public class InvalidAgeException : Exception
{
public int AttemptedAge { get; }
public InvalidAgeException(int age)
: base($"سن نامعتبر: {age}. سن باید بین 0 تا 150 باشد.")
{
AttemptedAge = age;
}
public InvalidAgeException(int age, string message) : base(message)
{
AttemptedAge = age;
}
}
// Exception سفارشی برای ایمیل نامعتبر
public class InvalidEmailException : Exception
{
public string AttemptedEmail { get; }
public InvalidEmailException(string email)
: base($"فرمت ایمیل نامعتبر: {email}")
{
AttemptedEmail = email;
}
}
public class Person
{
private int _age;
private string _email;
public int Age
{
get { return _age; }
set
{
if (value < 0 || value > 150)
{
throw new InvalidAgeException(value);
}
_age = value;
}
}
public string Email
{
get { return _email; }
set
{
if (string.IsNullOrEmpty(value) || !value.Contains("@"))
{
throw new InvalidEmailException(value ?? "null");
}
_email = value;
}
}
public string Name { get; set; }
}
class Program
{
static void Main()
{
try
{
Person person = new Person();
person.Name = "علی";
Console.Write("سن را وارد کنید: ");
int age = int.Parse(Console.ReadLine());
person.Age = age;
Console.Write("ایمیل را وارد کنید: ");
string email = Console.ReadLine();
person.Email = email;
Console.WriteLine($"✅ اطلاعات ثبت شد: {person.Name}, {person.Age} سال, {person.Email}");
}
catch (InvalidAgeException ex)
{
Console.WriteLine($"❌ خطای سن: {ex.Message}");
Console.WriteLine($"سن وارد شده: {ex.AttemptedAge}");
}
catch (InvalidEmailException ex)
{
Console.WriteLine($"❌ خطای ایمیل: {ex.Message}");
Console.WriteLine($"ایمیل وارد شده: {ex.AttemptedEmail}");
}
catch (FormatException)
{
Console.WriteLine("❌ لطفاً یک عدد معتبر برای سن وارد کنید!");
}
}
}
تکنیکهای Debugging 🔍
Debugging هنر پیدا کردن و رفع مشکلات در کد است. با تکنیکهای مختلف میتوانیم سریعتر مشکلات را شناسایی کنیم.
Console Debugging
- •
Console.WriteLine()- نمایش مقادیر - •
Console.ReadKey()- توقف برنامه - • رنگبندی پیامها
Debug Class
- •
Debug.WriteLine()- فقط در Debug mode - •
Debug.Assert()- بررسی شرایط - •
Debugger.Break()- توقف در debugger
using System;
using System.Diagnostics;
class Calculator
{
public static double Divide(double a, double b)
{
// Debug: نمایش مقادیر ورودی
Debug.WriteLine($"🔍 Divide called with a={a}, b={b}");
// Assert: بررسی شرط
Debug.Assert(b != 0, "مقسوم علیه نمیتواند صفر باشد!");
if (b == 0)
{
Debug.WriteLine("❌ تقسیم بر صفر تشخیص داده شد!");
throw new DivideByZeroException("نمیتوان بر صفر تقسیم کرد");
}
double result = a / b;
Debug.WriteLine($"✅ نتیجه محاسبه شد: {result}");
return result;
}
}
class DebugExample
{
static void Main()
{
// فعال کردن Debug listener برای نمایش در Console
Debug.Listeners.Add(new ConsoleTraceListener());
try
{
Console.WriteLine("=== شروع برنامه ===");
// تست عملیات عادی
Console.WriteLine($"10 ÷ 2 = {Calculator.Divide(10, 2)}");
// تست با صفر (باعث خطا میشود)
Console.WriteLine($"10 ÷ 0 = {Calculator.Divide(10, 0)}");
}
catch (DivideByZeroException ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"❌ خطا: {ex.Message}");
Console.ResetColor();
// Log کردن خطا با جزئیات
Debug.WriteLine($"Exception Details: {ex}");
}
finally
{
Console.WriteLine("=== پایان برنامه ===");
}
Console.WriteLine("\nPress any key to exit...");
Console.ReadKey();
}
}
💡 نکات مهم Debugging
- • همیشه پیامهای Debug را واضح و مفید بنویسید
- • از رنگهای مختلف برای انواع پیامها استفاده کنید
- • Debug.WriteLine فقط در حالت Debug کار میکند
- • در نسخه نهایی، کدهای Debug حذف میشوند
👤 مثال اضافی: کلاس Person با Debugging
در این مثال، یک کلاس Person ایجاد میکنیم که شامل Exception Handling و Debugging است.
// کلاس Person با اعتبارسنجی سن
public class Person
{
private int _age;
public int Age
{
get { return _age; }
set
{
if (value < 0 || value > 150)
{
throw new InvalidAgeException($"سن نامعتبر: {value}");
}
_age = value;
}
}
public string Name { get; set; }
}
class Program
{
static void Main()
{
try
{
Person person = new Person();
person.Name = "علی";
// استفاده از Debug برای نمایش اطلاعات
Debug.WriteLine($"در حال تنظیم سن برای {person.Name}");
Console.Write("سن را وارد کنید: ");
int age = int.Parse(Console.ReadLine());
person.Age = age; // ممکن است Exception سفارشی پرتاب کند
Console.WriteLine($"سلام {person.Name}، شما {person.Age} سال دارید.");
}
catch (InvalidAgeException ex)
{
Console.WriteLine($"خطای سن: {ex.Message}");
// Log کردن خطا برای بررسی بعدی
Debug.WriteLine($"خطای سن رخ داد: {ex}");
}
catch (FormatException)
{
Console.WriteLine("لطفاً یک عدد معتبر وارد کنید!");
}
// نمایش اطلاعات سیستم برای Debugging
Console.WriteLine($"\nاطلاعات سیستم:");
Console.WriteLine($"زمان اجرا: {DateTime.Now}");
Console.WriteLine($"نسخه .NET: {Environment.Version}");
}
}
تمرینهای عملی! 🧠💪
تمرین ۱: سیستم مدیریت حساب بانکی 🏦
یک کلاس BankAccount بسازید که شامل موارد زیر باشد:
- Exception های سفارشی:
InsufficientFundsExceptionوInvalidAmountException - متدهای
DepositوWithdrawبا مدیریت خطا - محدودیت حداقل موجودی و حداکثر مبلغ تراکنش
- سیستم لاگ برای تمام تراکنشها
جواب تمرین ۱
using System;
using System.Collections.Generic;
using System.Diagnostics;
// Exception های سفارشی
public class InsufficientFundsException : Exception
{
public decimal RequestedAmount { get; }
public decimal AvailableBalance { get; }
public InsufficientFundsException(decimal requested, decimal available)
: base($"موجودی کافی نیست. مبلغ درخواستی: {requested:C}, موجودی: {available:C}")
{
RequestedAmount = requested;
AvailableBalance = available;
}
}
public class InvalidAmountException : Exception
{
public decimal AttemptedAmount { get; }
public InvalidAmountException(decimal amount)
: base($"مبلغ نامعتبر: {amount:C}. مبلغ باید مثبت و کمتر از 10,000,000 تومان باشد.")
{
AttemptedAmount = amount;
}
}
public class BankAccount
{
private decimal _balance;
private List _transactionLog;
public string AccountNumber { get; }
public string OwnerName { get; }
public decimal Balance => _balance;
public decimal MinimumBalance { get; } = 50000; // حداقل موجودی
public decimal MaxTransactionAmount { get; } = 10000000; // حداکثر تراکنش
public BankAccount(string accountNumber, string ownerName, decimal initialBalance)
{
AccountNumber = accountNumber;
OwnerName = ownerName;
_balance = initialBalance;
_transactionLog = new List();
LogTransaction($"حساب ایجاد شد با موجودی اولیه: {initialBalance:C}");
}
public void Deposit(decimal amount)
{
ValidateAmount(amount);
_balance += amount;
LogTransaction($"واریز: {amount:C} - موجودی جدید: {_balance:C}");
Console.WriteLine($"✅ مبلغ {amount:C} واریز شد. موجودی فعلی: {_balance:C}");
}
public void Withdraw(decimal amount)
{
ValidateAmount(amount);
if (_balance - amount < MinimumBalance)
{
throw new InsufficientFundsException(amount, _balance - MinimumBalance);
}
_balance -= amount;
LogTransaction($"برداشت: {amount:C} - موجودی جدید: {_balance:C}");
Console.WriteLine($"✅ مبلغ {amount:C} برداشت شد. موجودی فعلی: {_balance:C}");
}
private void ValidateAmount(decimal amount)
{
if (amount <= 0 || amount > MaxTransactionAmount)
{
throw new InvalidAmountException(amount);
}
}
private void LogTransaction(string transaction)
{
string logEntry = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {transaction}";
_transactionLog.Add(logEntry);
Debug.WriteLine(logEntry);
}
public void ShowTransactionHistory()
{
Console.WriteLine($"\n📋 تاریخچه تراکنشهای حساب {AccountNumber}:");
foreach (var transaction in _transactionLog)
{
Console.WriteLine(transaction);
}
}
}
class Program
{
static void Main()
{
try
{
BankAccount account = new BankAccount("123456789", "علی احمدی", 1000000);
Console.WriteLine($"حساب {account.OwnerName} ایجاد شد.");
Console.WriteLine($"موجودی اولیه: {account.Balance:C}\n");
// تست واریز
account.Deposit(500000);
// تست برداشت موفق
account.Withdraw(300000);
// تست برداشت ناموفق (موجودی کافی نیست)
account.Withdraw(1500000);
}
catch (InsufficientFundsException ex)
{
Console.WriteLine($"❌ {ex.Message}");
Console.WriteLine($"مبلغ قابل برداشت: {ex.AvailableBalance:C}");
}
catch (InvalidAmountException ex)
{
Console.WriteLine($"❌ {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"❌ خطای غیرمنتظره: {ex.Message}");
}
}
}
تمرین ۲: سیستم مدیریت فایل با Exception Handling 📁
یک کلاس FileManager بسازید که بتواند:
- فایلها را بخواند، بنویسد و کپی کند
- تمام خطاهای مربوط به فایل را مدیریت کند
- سیستم لاگ برای عملیاتها داشته باشد
- از تکنیکهای Debugging استفاده کند
جواب تمرین ۲
using System;
using System.IO;
using System.Diagnostics;
public class FileOperationException : Exception
{
public string FileName { get; }
public string Operation { get; }
public FileOperationException(string fileName, string operation, string message)
: base($"خطا در {operation} فایل '{fileName}': {message}")
{
FileName = fileName;
Operation = operation;
}
}
public class FileManager
{
private static void LogOperation(string operation, string details = "")
{
string logMessage = $"[{DateTime.Now:HH:mm:ss}] {operation}";
if (!string.IsNullOrEmpty(details))
logMessage += $" - {details}";
Debug.WriteLine(logMessage);
Console.WriteLine($"📝 {logMessage}");
}
public static string ReadFile(string filePath)
{
try
{
LogOperation("شروع خواندن فایل", filePath);
if (!File.Exists(filePath))
{
throw new FileOperationException(filePath, "خواندن", "فایل وجود ندارد");
}
string content = File.ReadAllText(filePath);
LogOperation("فایل با موفقیت خوانده شد", $"تعداد کاراکتر: {content.Length}");
return content;
}
catch (UnauthorizedAccessException)
{
throw new FileOperationException(filePath, "خواندن", "دسترسی غیرمجاز");
}
catch (IOException ex)
{
throw new FileOperationException(filePath, "خواندن", ex.Message);
}
}
public static void WriteFile(string filePath, string content)
{
try
{
LogOperation("شروع نوشتن فایل", filePath);
// بررسی مسیر
string directory = Path.GetDirectoryName(filePath);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
LogOperation("پوشه ایجاد شد", directory);
}
File.WriteAllText(filePath, content);
LogOperation("فایل با موفقیت نوشته شد", $"تعداد کاراکتر: {content.Length}");
}
catch (UnauthorizedAccessException)
{
throw new FileOperationException(filePath, "نوشتن", "دسترسی غیرمجاز");
}
catch (IOException ex)
{
throw new FileOperationException(filePath, "نوشتن", ex.Message);
}
}
public static void CopyFile(string sourcePath, string destinationPath)
{
try
{
LogOperation("شروع کپی فایل", $"{sourcePath} -> {destinationPath}");
if (!File.Exists(sourcePath))
{
throw new FileOperationException(sourcePath, "کپی", "فایل مبدأ وجود ندارد");
}
File.Copy(sourcePath, destinationPath, true);
LogOperation("فایل با موفقیت کپی شد");
}
catch (UnauthorizedAccessException)
{
throw new FileOperationException(destinationPath, "کپی", "دسترسی غیرمجاز");
}
catch (IOException ex)
{
throw new FileOperationException(destinationPath, "کپی", ex.Message);
}
}
}
class Program
{
static void Main()
{
// فعال کردن Debug output
Debug.Listeners.Add(new ConsoleTraceListener());
try
{
Console.WriteLine("=== تست سیستم مدیریت فایل ===");
// تست نوشتن فایل
string testFile = "test.txt";
string content = "سلام! این یک فایل تست است.\nخط دوم فایل.";
FileManager.WriteFile(testFile, content);
// تست خواندن فایل
string readContent = FileManager.ReadFile(testFile);
Console.WriteLine($"\n📄 محتوای فایل:\n{readContent}\n");
// تست کپی فایل
FileManager.CopyFile(testFile, "backup_test.txt");
// تست خطا - خواندن فایل غیرموجود
FileManager.ReadFile("nonexistent.txt");
}
catch (FileOperationException ex)
{
Console.WriteLine($"❌ {ex.Message}");
Console.WriteLine($"فایل: {ex.FileName}, عملیات: {ex.Operation}");
}
catch (Exception ex)
{
Console.WriteLine($"❌ خطای غیرمنتظره: {ex.Message}");
}
finally
{
Console.WriteLine("\n=== پایان تست ===");
}
}
}
💡 نکات مهم برای حل تمرینها
- • همیشه Exception های خاص را قبل از Exception عمومی catch کنید
- • از finally برای پاک کردن منابع استفاده کنید
- • پیامهای خطا را واضح و کاربرپسند بنویسید
- • از Debug.WriteLine برای ردیابی مشکلات استفاده کنید
- • Exception های سفارشی باید اطلاعات مفید ارائه دهند