خیلی خوب، رسیدیم به درس یازدهم: کلاسهای استاتیک (Static) و نمونه (Instance).
این درس خیلی مهم است چون خیلی از برنامهنویسان تازهکار فرق بین static و غیر static را اشتباه میگیرند.
یک پروژه جدید بسازید (dotnet new console) و کدها را همراه من بنویسید و اجرا کنید.
---
📘 درس یازدهم – کلاسهای استاتیک و نمونه
🎯 سرفصلها:
1. تفاوت استاتیک و نمونه (Instance)
2. فیلد استاتیک
3. متد استاتیک
4. کلاس استاتیک
5. سازنده استاتیک
6. کلاس Math و Console مثال واقعی
7. تمرین جامع
---
⏱️ قسمت 1: تفاوت استاتیک و نمونه
تصور کنید کلاس یک قالب کلوچه است:
مفهوم توضیح
نمونه (Instance) هر کلوچه (با شکل و اندازه خودش)
استاتیک (Static) قالب کلوچه (یک نسخه مشترک بین همه)
csharp
class Cookie
{
// فیلد نمونه (هر کلوچه یک مقدار جدا دارد)
public string Shape { get; set; }
// فیلد استاتیک (بین همه کلوچهها مشترک است)
public static string MoldType = "Metal Mold";
}
// استفاده
Cookie cookie1 = new Cookie();
Cookie cookie2 = new Cookie();
cookie1.Shape = "Star";
cookie2.Shape = "Heart";
Console.WriteLine(cookie1.Shape); // Star
Console.WriteLine(cookie2.Shape); // Heart
// فیلد استاتیک: از طریق کلاس دسترسی داریم، نه نمونه
Console.WriteLine(Cookie.MoldType); // Metal Mold
📌 نکته طلایی:
· اعضای نمونه (Instance) به هر شیء تعلق دارند (هر کدام مقدار خودش را دارد)
· اعضای استاتیک (Static) به کلاس تعلق دارند (یک مقدار برای همه)
---
⏱️ قسمت 2: فیلد استاتیک
فیلدهای استاتیک بین همه نمونههای کلاس مشترک هستند.
csharp
class BankAccount
{
public string Owner { get; set; }
public double Balance { get; set; }
// فیلد استاتیک - تعداد کل حسابهای ساخته شده
public static int TotalAccounts = 0;
public BankAccount(string owner, double balance)
{
Owner = owner;
Balance = balance;
TotalAccounts++; // هر بار یک حساب ساخته میشود، این عدد زیاد میشود
}
}
// استفاده
BankAccount acc1 = new BankAccount("Ali", 1000);
BankAccount acc2 = new BankAccount("Sara", 2000);
BankAccount acc3 = new BankAccount("Reza", 1500);
Console.WriteLine($"Total accounts: {BankAccount.TotalAccounts}"); // 3
// ❌ acc1.TotalAccounts - نمیتوان از نمونه دسترسی داشت
---
⏱️ قسمت 3: متد استاتیک
متدهای استاتیک به کلاس تعلق دارند، نه به نمونه.
csharp
class MathHelper
{
// متد استاتیک - نیازی به ساختن شیء نیست
public static int Add(int a, int b)
{
return a + b;
}
public static int Multiply(int a, int b)
{
return a * b;
}
public static bool IsEven(int number)
{
return number % 2 == 0;
}
}
// استفاده - بدون ساختن شیء!
int sum = MathHelper.Add(5, 3);
int product = MathHelper.Multiply(4, 5);
bool even = MathHelper.IsEven(10);
Console.WriteLine($"Sum: {sum}"); // 8
Console.WriteLine($"Product: {product}"); // 20
Console.WriteLine($"Is 10 even? {even}"); // True
فرق متد استاتیک و نمونه:
csharp
class Calculator
{
// متد نمونه (Instance)
public int InstanceAdd(int a, int b)
{
return a + b;
}
// متد استاتیک (Static)
public static int StaticAdd(int a, int b)
{
return a + b;
}
}
// استفاده
Calculator calc = new Calculator(); // باید شیء بسازیم
int result1 = calc.InstanceAdd(5, 3); // ✅
int result2 = Calculator.StaticAdd(5, 3); // ✅ بدون ساختن شیء
// int result3 = calc.StaticAdd(5, 3); // ❌ نمیتوان از نمونه صدا زد
---
⏱️ قسمت 4: کلاس استاتیک
کلاس استاتیک = کلاسی که نمیتوان از آن شیء ساخت.
`csharp static class Utility { // فقط میتواند اعضای استاتیک داشته باشد public static double PI = 3.14159; public static double CircleArea(double radius) { return PI * radius * radius; } public static string ToTitleCase(string text) { if (string.IsNullOrEmpty(text)) return text; return char.ToUpper(text[0]) + text.Substring(1).ToLower(); } } // استفاده // Utility u = new Utility(); // ❌ خطا - نمیتوان از کلاس استاتیک شیء ساخت double area = Utility.CircleArea(5); string title = Utility.ToTitleCase("hello world");
Console.WriteLine($"Area: {area:F2}"); // 78.54
Console.WriteLine($"Title: {title}"); // Hello world
📌 قوانین کلاس استاتیک:
· نمیتوان از آن نمونه (Object) ساخت
· فقط میتواند اعضای استاتیک داشته باشد
· نمیتواند سازنده نمونه (instance constructor) داشته باشد
· به طور خودکار sealed است (نمیتوان از آن ارث برد)
---
⏱️ قسمت 5: سازنده استاتیک (Static Constructor)
سازنده استاتیک یک بار و قبل از اولین استفاده از کلاس اجرا میشود.
csharp
class Database
{
public static string ConnectionString;
public static int ConnectionCount;
// سازنده استاتیک - یک بار اجرا میشود
static Database()
{
Console.WriteLine("Static constructor called");
ConnectionString = "Server=localhost;Database=MyDB";
ConnectionCount = 0;
}
// متد استاتیک
public static void Connect()
{
ConnectionCount++;
Console.WriteLine($"Connected to {ConnectionString}");
Console.WriteLine($"Total connections: {ConnectionCount}");
}
}
// استفاده
Console.WriteLine("Before first use");
Database.Connect(); // اولین استفاده → سازنده استاتیک اجرا میشود
Database.Connect(); // سازنده استاتیک دیگر اجرا نمیشود
Database.Connect();
خروجی:
Before first use
Static constructor called
Connected to Server=localhost;Database=MyDB
Total connections: 1
Connected to Server=localhost;Database=MyDB
Total connections: 2
Connected to Server=localhost;Database=MyDB
Total connections: 3
---
⏱️ قسمت 6: مثال واقعی – کلاس Math و Console
کلاسهایی که روزانه استفاده میکنیم، استاتیک هستند:
csharp
// کلاس Math - کاملاً استاتیک
double max = Math.Max(10, 20); // 20
double min = Math.Min(10, 20); // 10
double sqrt = Math.Sqrt(25); // 5
double power = Math.Pow(2, 3); // 8
double pi = Math.PI; // 3.14159...
double abs = Math.Abs(-5); // 5
// کلاس Console - استاتیک
Console.WriteLine("Hello"); // چاپ
string input = Console.ReadLine(); // ورودی
// کلاس Convert - استاتیک
int num = Convert.ToInt32("123");
double d = Convert.ToDouble("3.14");
---
⏱️ قسمت 7: ترکیب استاتیک و نمونه
میتوانیم در یک کلاس هم عضو استاتیک و هم عضو نمونه داشته باشیم.
csharp
class Employee
{
// فیلدهای نمونه
public string Name { get; set; }
public int Id { get; set; }
public double Salary { get; set; }
// فیلد استاتیک
private static int nextId = 1000;
public static string CompanyName = "TechCorp";
public static int TotalEmployees = 0;
// سازنده نمونه
public Employee(string name, double salary)
{
Name = name;
Salary = salary;
Id = nextId++;
TotalEmployees++;
Console.WriteLine($"Employee {Name} created with ID {Id}");
}
// متد نمونه (میتواند به استاتیک دسترسی داشته باشد)
public void ShowInfo()
{
Console.WriteLine($"ID: {Id}, Name: {Name}, Salary: {Salary:C}");
Console.WriteLine($"Company: {Employee.CompanyName}");
Console.WriteLine($"Total employees: {Employee.TotalEmployees}");
}
// متد استاتیک (نمیتواند به اعضای نمونه دسترسی داشته باشد)
public static void ShowCompanyInfo()
{
Console.WriteLine($"Company: {CompanyName}");
Console.WriteLine($"Total employees: {TotalEmployees}");
// Console.WriteLine(Name); // ❌ خطا - Name عضو نمونه است
}
}
// استفاده
Employee emp1 = new Employee("Ali", 5000000);
Employee emp2 = new Employee("Sara", 6000000);
Employee emp3 = new Employee("Reza", 5500000);
Console.WriteLine();
emp1.ShowInfo();
Console.WriteLine();
Employee.ShowCompanyInfo();
---
⏱️ قسمت 8: متد استاتیک در کلاس غیر استاتیک
کلاس غیر استاتیک میتواند متد استاتیک داشته باشد.
csharp
class StringHelper
{
// متد استاتیک
public static string Reverse(string text)
{
char[] chars = text.ToCharArray();
Array.Reverse(chars);
return new string(chars);
}
public static int CountWords(string text)
{
if (string.IsNullOrEmpty(text))
return 0;
return text.Split(' ', StringSplitOptions.RemoveEmptyEntries).Length;
}
// متد نمونه
public string AddExclamation(string text)
{
return text + "!!!";
}
}
// استفاده
string reversed = StringHelper.Reverse("hello");
Console.WriteLine(reversed); // olleh
int wordCount = StringHelper.CountWords("Hello world from C#");
Console.WriteLine(wordCount); // 4
// برای متد نمونه باید شیء بسازیم
StringHelper helper = new StringHelper();
string excited = helper.AddExclamation("Hello");
Console.WriteLine(excited); // Hello!!!
---
✅ تمرین نهایی درس یازدهم
برنامهای بنویسید که:
1. کلاس Product با فیلدهای نمونه (Name, Price) و فیلد استاتیک (TotalProducts, TotalValue)
2. متد نمونه ShowInfo()
3. متد استاتیک ShowSummary()
4. هر بار یک محصول جدید ساخته شود، TotalProducts و TotalValue بهروز شود
csharp
using System;
class Product
{
// فیلدهای نمونه
public string Name { get; set; }
public double Price { get; set; }
// فیلدهای استاتیک
private static int totalProducts = 0;
private static double totalValue = 0;
// سازنده
public Product(string name, double price)
{
Name = name;
Price = price;
totalProducts++;
totalValue += price;
Console.WriteLine($"Product '{name}' created with price {price:C}");
}
// متد نمونه
public void ShowInfo()
{
Console.WriteLine($"Product: {Name}, Price: {Price:C}");
}
// متد استاتیک برای نمایش خلاصه
public static void ShowSummary()
{
Console.WriteLine("\n=== Store Summary ===");
Console.WriteLine($"Total Products: {totalProducts}");
Console.WriteLine($"Total Inventory Value: {totalValue:C}");
Console.WriteLine($"Average Price: {(totalProducts > 0 ? totalValue / totalProducts : 0):C}");
}
// متد استاتیک برای محاسبه تخفیف
public static double ApplyDiscount(double price, double discountPercent)
{
return price * (1 - discountPercent / 100);
}
}
class Program
{
static void Main()
{
// ساختن محصولات
Product p1 = new Product("Laptop", 15000000);
Product p2 = new Product("Mouse", 250000);
Product p3 = new Product("Keyboard", 850000);
Product p4 = new Product("Monitor", 5500000);
Console.WriteLine();
// نمایش اطلاعات هر محصول
p1.ShowInfo();
p2.ShowInfo();
p3.ShowInfo();
p4.ShowInfo();
// نمایش خلاصه فروشگاه
Product.ShowSummary();
// استفاده از متد استاتیک تخفیف
double discountedPrice = Product.ApplyDiscount(15000000, 15);
Console.WriteLine($"\nLaptop with 15% discount: {discountedPrice:C}");
Console.WriteLine("\nPress Enter to exit...");
Console.ReadLine();
}
}
---
📌 جمعبندی درس یازدهم
مفهوم مثال دسترسی
فیلد نمونه public string Name از طریق شیء
فیلد استاتیک public static int Count از طریق کلاس
متد نمونه public void Show() از طریق شیء
متد استاتیک public static void Calc() از طریق کلاس
کلاس استاتیک static class Utility نمیتوان شیء ساخت
قوانین مهم:
1. متد استاتیک نمیتواند به عضو نمونه دسترسی داشته باشد
csharp
static void Method() { Name = "Ali"; } // ❌ اگر Name نمونه باشد
2. متد نمونه میتواند به عضو استاتیک دسترسی داشته باشد
csharp
void Method() { Count = 10; } // ✅ اگر Count استاتیک باشد
`3. کلاس استاتیک فقط عضو استاتیک میتواند داشته باشد --- 🧪 تمرین برای شما 1. کلاس Counter بسازید با: · فیلد استاتیک totalCount
· متد استاتیک Increment()
· متد استاتیک GetCount()
· از چند جا صدا بزنید و نتیجه را ببینید
2. کلاس Configuration بسازید با:
· فیلدهای استاتیک: AppName, Version, Theme
· سازنده استاتیک برای مقداردهی اولیه
3. برنامهای بنویسید که تعداد دفعاتی که یک متد صدا زده شده را با فیلد استاتیک بشمارد
---
اگر این درس را کامل فهمیدید، بگویید تا درس دوازدهم (Enum و Struct) را شروع کنیم. سوالی بود بپرسید.
خیلی خوب، رسیدیم به درس دوازدهم: Enum (شمارندهها) و Struct (ساختارها).
این دو نوع داده به شما کمک میکنند کد خواناتر و منظمتری بنویسید.
یک پروژه جدید بسازید (dotnet new console) و کدها را همراه من بنویسید و اجرا کنید.
---
📘 درس دوازدهم – Enum و Struct
🎯 سرفصلها:
1. Enum چیست و چرا نیاز داریم؟
2. تعریف و استفاده از Enum
3. مقداردهی عددی به Enum
4. تبدیل Enum به عدد و متن
5. Struct چیست؟
6. تفاوت Struct با Class
7. استفاده از Struct
8. تمرین جامع
---
⏱️ قسمت 1: Enum چیست؟
Enum = مجموعهای از ثابتهای نامدار
بدون Enum (کد بد):
csharp
int userRole = 1; // 1=admin, 2=user, 3=guest
if (userRole == 1)
{
Console.WriteLine("Welcome Admin");
}
// مشکل: عدد 1 یعنی چه؟ یادم میرود!
با Enum (کد خوب):
csharp
enum UserRole
{
Admin,
User,
Guest
}
UserRole role = UserRole.Admin;
if (role == UserRole.Admin)
{
Console.WriteLine("Welcome Admin");
}
---
⏱️ قسمت 2: تعریف و استفاده از Enum
csharp
// تعریف Enum (معمولاً بیرون از کلاس)
enum Days
{
Saturday,
Sunday,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday
}
enum Status
{
Pending,
Approved,
Rejected,
Shipped
}
// استفاده
Days today = Days.Wednesday;
Status orderStatus = Status.Approved;
Console.WriteLine($"Today is: {today}");
Console.WriteLine($"Order status: {orderStatus}");
// استفاده در شرط
if (orderStatus == Status.Approved)
{
Console.WriteLine("Your order is approved!");
}
---
⏱️ قسمت 3: مقداردهی عددی به Enum
به طور پیشفرض، اعضای Enum از 0 شروع میشوند.
csharp
enum Priority
{
Low = 1, // مقدار 1
Medium = 2, // مقدار 2
High = 3, // مقدار 3
Urgent = 10 // مقدار 10
}
enum Color
{
Red = 10,
Green = 20,
Blue = 30
}
// استفاده
Priority taskPriority = Priority.High;
Console.WriteLine(taskPriority); // High
Console.WriteLine((int)taskPriority); // 3
Priority urgent = Priority.Urgent;
Console.WriteLine((int)urgent); // 10
مقداردهی خودکار:
csharp
enum ErrorCode
{
None = 0,
NotFound = 100,
Unauthorized = 101,
ServerError = 500
}
---
⏱️ قسمت 4: تبدیل Enum به عدد و متن
csharp
enum Season
{
Spring = 1,
Summer = 2,
Autumn = 3,
Winter = 4
}
// Enum به عدد
Season current = Season.Summer;
int seasonNumber = (int)current;
Console.WriteLine(seasonNumber); // 2
// عدد به Enum
int input = 3;
Season parsedSeason = (Season)input;
Console.WriteLine(parsedSeason); // Autumn
// Enum به متن
string seasonName = current.ToString();
Console.WriteLine(seasonName); // Summer
// متن به Enum
string userInput = "Winter";
Season selected = (Season)Enum.Parse(typeof(Season), userInput);
Console.WriteLine(selected); // Winter
// روش امن برای تبدیل (بدون خطا)
if (Enum.TryParse("Spring", out Season result))
{
Console.WriteLine($"Parsed: {result}");
}
---
⏱️ قسمت 5: Struct چیست؟
Struct = یک نوع مقدار (Value Type) که دادههای کوچک را نگهداری میکند.
تعریف Struct:
csharp
struct Point
{
public int X;
public int Y;
public Point(int x, int y)
{
X = x;
Y = y;
}
public double DistanceFromOrigin()
{
return Math.Sqrt(X * X + Y * Y);
}
}
// استفاده
Point p1;
p1.X = 10;
p1.Y = 20;
Point p2 = new Point(5, 7);
Point p3 = new Point(); // تمام فیلدها پیشفرض (0)
Console.WriteLine($"p1: ({p1.X}, {p1.Y})");
Console.WriteLine($"p2: ({p2.X}, {p2.Y})");
Console.WriteLine($"p3: ({p3.X}, {p3.Y})");
Console.WriteLine($"Distance: {p2.DistanceFromOrigin():F2}");
---
⏱️ قسمت 6: تفاوت Struct با Class (خیلی مهم)
ویژگی Class Struct
نوع Reference Type (ارجاعی) Value Type (مقداری)
مکان در حافظه Heap Stack
ارثبری دارد ندارد
سازنده پیشفرض دارد نمیتوان داشت
مقایسه مرجع را مقایسه میکند مقدار را مقایسه میکند
سرعت برای داده کوچک کندتر سریعتر
استفاده برای دادههای بزرگ دادههای کوچک (کمتر از 16 بایت)
`csharp // کلاس (Reference Type) class PointClass { public int X { get; set; } public int Y { get; set; } public PointClass(int x, int y) { X = x; Y = y; } }
// Struct (Value Type)
struct PointStruct
{
public int X { get; set; }
public int Y { get; set; }
public PointStruct(int x, int y)
{
X = x;
Y = y;
}
}
// تفاوت در عمل
PointClass c1 = new PointClass(5, 5);
PointClass c2 = c1; // c2 به همان شیء اشاره میکند
c2.X = 10;
Console.WriteLine($"c1.X = {c1.X}"); // 10 (تغییر کرد!)
PointStruct s1 = new PointStruct(5, 5);
PointStruct s2 = s1; // کپی از مقدار
s2.X = 10;
Console.WriteLine($"s1.X = {s1.X}"); // 5 (تغییر نکرد!)
---
⏱️ قسمت 7: مثالهای کاربردی Struct
Struct برای مختصات RGB:
csharp
struct RGBColor
{
public byte Red;
public byte Green;
public byte Blue;
public RGBColor(byte red, byte green, byte blue)
{
Red = red;
Green = green;
Blue = blue;
}
public string GetHexCode()
{
return $"#{Red:X2}{Green:X2}{Blue:X2}";
}
public string GetName()
{
if (Red == 255 && Green == 0 && Blue == 0) return "Red";
if (Red == 0 && Green == 255 && Blue == 0) return "Green";
if (Red == 0 && Green == 0 && Blue == 255) return "Blue";
if (Red == 255 && Green == 255 && Blue == 255) return "White";
if (Red == 0 && Green == 0 && Blue == 0) return "Black";
return "Custom Color";
}
}
// استفاده
RGBColor red = new RGBColor(255, 0, 0);
RGBColor custom = new RGBColor(128, 200, 50);
Console.WriteLine(red.GetHexCode()); // #FF0000
Console.WriteLine(red.GetName()); // Red
Console.WriteLine(custom.GetHexCode()); // #80C832
Console.WriteLine(custom.GetName()); // Custom Color
Struct برای محدوده (Range):
csharp
struct Range
{
public int Start;
public int End;
public Range(int start, int end)
{
if (start > end)
throw new ArgumentException("Start must be less than or equal to End");
Start = start;
End = end;
}
public int Length => End - Start + 1;
public bool Contains(int value)
{
return value >= Start && value <= End;
}
public int[] ToArray()
{
int[] result = new int[Length];
for (int i = 0; i < Length; i++)
{
result[i] = Start + i;
}
return result;
}
}
// استفاده
Range range = new Range(1, 10);
Console.WriteLine($"Length: {range.Length}"); // 10
Console.WriteLine($"Contains 5: {range.Contains(5)}"); // True
Console.WriteLine($"Contains 15: {range.Contains(15)}"); // False
int[] numbers = range.ToArray();
Console.WriteLine(string.Join(", ", numbers)); // 1,2,3,4,5,6,7,8,9,10
---
⏱️ قسمت 8: Struct در مقابل Class – کدام را استفاده کنیم؟
از Struct استفاده کنید وقتی:
· دادهها کوچک هستند (کمتر از 16 بایت)
· نیاز به کپی کردن مکرر دارید
· نیازی به ارثبری ندارید
· نوع داده ساده و مستقل است (مثل نقطه، رنگ، محدوده)
از Class استفاده کنید وقتی:
· دادهها بزرگ هستند
· نیاز به ارثبری دارید
· نیاز به رفتارهای پیچیده دارید
· شیء باید قابلیت تغییر (Mutable) داشته باشد
csharp
// خوب برای Struct (داده کوچک)
struct Coordinate
{
public double Latitude;
public double Longitude;
}
struct Size
{
public int Width;
public int Height;
}
// خوب برای Class (داده بزرگ و رفتار پیچیده)
class Customer
{
public string Name { get; set; }
public string Email { get; set; }
public List<Order> Orders { get; set; }
}
---
✅ تمرین نهایی درس دوازدهم
برنامهای بنویسید که:
1. Enum ای برای OrderStatus (Pending, Processing, Shipped, Delivered, Cancelled)
2. Struct ای برای Product (Name, Price, Quantity)
3. Struct ای برای Order (Id, CustomerName, List<Product>, Status)
4. امکانات: افزودن محصول، نمایش سفارش، تغییر وضعیت
csharp
using System;
using System.Collections.Generic;
enum OrderStatus
{
Pending,
Processing,
Shipped,
Delivered,
Cancelled
}
struct Product
{
public string Name;
public double Price;
public int Quantity;
public Product(string name, double price, int quantity)
{
Name = name;
Price = price;
Quantity = quantity;
}
public double TotalPrice => Price * Quantity;
public void Display()
{
Console.WriteLine($" - {Name}: {Quantity} x {Price:C} = {TotalPrice:C}");
}
}
struct Order
{
public int Id;
public string CustomerName;
public List<Product> Products;
public OrderStatus Status;
public Order(int id, string customerName)
{
Id = id;
CustomerName = customerName;
Products = new List<Product>();
Status = OrderStatus.Pending;
}
public void AddProduct(Product product)
{
Products.Add(product);
Console.WriteLine($"Added {product.Quantity}x {product.Name} to order #{Id}");
}
public double GetTotal()
{
double total = 0;
foreach (Product p in Products)
{
total += p.TotalPrice;
}
return total;
}
public void ChangeStatus(OrderStatus newStatus)
{
Status = newStatus;
Console.WriteLine($"Order #{Id} status changed to {Status}");
}
public void Display()
{
Console.WriteLine($"\n=== Order #{Id} ===");
Console.WriteLine($"Customer: {CustomerName}");
Console.WriteLine($"Status: {Status}");
Console.WriteLine("Products:");
if (Products.Count == 0)
{
Console.WriteLine(" (empty)");
}
else
{
foreach (Product p in Products)
{
p.Display();
}
}
Console.WriteLine($"Total: {GetTotal():C}");
Console.WriteLine("=================");
}
}
class Program
{
static void Main()
{
// ساخت سفارش
Order order1 = new Order(1001, "Ali Mohammadi");
Order order2 = new Order(1002, "Sara Ahmadi");
// افزودن محصولات
Console.WriteLine("=== Adding Products ===");
order1.AddProduct(new Product("Laptop", 15000000, 1));
order1.AddProduct(new Product("Mouse", 250000, 2));
order2.AddProduct(new Product("Keyboard", 850000, 1));
order2.AddProduct(new Product("Monitor", 5500000, 1));
order2.AddProduct(new Product("Headphones", 1200000, 1));
// نمایش سفارشها
order1.Display();
order2.Display();
// تغییر وضعیت
Console.WriteLine("\n=== Status Updates ===");
order1.ChangeStatus(OrderStatus.Processing);
order1.ChangeStatus(OrderStatus.Shipped);
order2.ChangeStatus(OrderStatus.Delivered);
// نمایش نهایی
order1.Display();
order2.Display();
Console.WriteLine("\nPress Enter to exit...");
Console.ReadLine();
}
}
`--- 📌 جمعبندی درس دوازدهم مفهوم کاربرد مثال Enum مجموعه ثابتهای نامدار Days.Monday Struct نوع مقدار برای دادههای کوچک Point p = new Point(10, 20) Class نوع مرجع برای دادههای بزرگ Customer c = new Customer() نکات کلیدی Enum: · مقادیر از 0 شروع میشوند (قابل تغییر) · میتوان به عدد و متن تبدیل کرد · قابلیت خواندن کد را بالا میبرد نکات کلیدی Struct: · نوع مقدار (Value Type) است · روی Stack ذخیره میشود · سریعتر از Class برای دادههای کوچک · نمیتوان از آن ارث برد · مقایسه بر اساس مقادیر است نه مرجع --- 🧪 تمرین برای شما 1. Enum ای برای Month (12 ماه) بسازید و برنامهای بنویسید که تعداد روزهای هر ماه را چاپ کند 2. Struct ای برای Time (Hour, Minute, Second) بسازید با متد ToSeconds() و ToString() 3. Struct ای برای Fraction (صورت و مخرج) بسازید با متدهای جمع، تفریق، ضرب و تقسیم 4. تفاوت عملی Struct و Class را با یک مثال نشان دهید (نشان دهید کپی شدن چطور کار میکند) --- اگر این درس را کامل فهمیدید، بگویید تا درس سیزدهم (Delegates و Events) را شروع کنیم. سوالی بود بپرسید.
خیلی خوب، رسیدیم به درس سیزدهم: Delegates (نمایندهها) و Events (رویدادها).
این درس کمی پیشرفتهتر است اما خیلی کاربردی. Delegates به شما اجازه میدهند متدها را به عنوان متغیر استفاده کنید.
یک پروژه جدید بسازید (dotnet new console) و کدها را همراه من بنویسید و اجرا کنید.
---
📘 درس سیزدهم – Delegates و Events
🎯 سرفصلها:
1. Delegate چیست؟
2. تعریف و استفاده از Delegate
3. Multicast Delegate (چند متدی)
4. Func، Action، Predicate (Delegateهای آماده)
5. Event چیست؟
6. تفاوت Event با Delegate
7. مثال واقعی (دکمه کلیک)
8. تمرین جامع
---
⏱️ قسمت 1: Delegate چیست؟
Delegate = یک متغیر که میتواند به یک متد اشاره کند (اشارهگر به متد)
csharp
// 1. تعریف Delegate (امضای متد را مشخص میکند)
delegate int MathOperation(int a, int b);
// 2. متدهایی با همان امضا
int Add(int x, int y)
{
return x + y;
}
int Multiply(int x, int y)
{
return x * y;
}
// 3. استفاده
MathOperation operation = Add; // اشاره به متد Add
int result1 = operation(5, 3); // 8
Console.WriteLine($"Add: {result1}");
operation = Multiply; // تغییر اشاره به متد Multiply
int result2 = operation(5, 3); // 15
Console.WriteLine($"Multiply: {result2}");
📌 Delegate مانند یک قالب است:
· امضای متد را مشخص میکند (نوع ورودی و خروجی)
· هر متدی با همین امضا را میتوان به آن اختصاص داد
---
⏱️ قسمت 2: کاربرد واقعی Delegate
csharp
delegate bool FilterFunction(int number);
// متدهای فیلتر
bool IsEven(int n) => n % 2 == 0;
bool IsGreaterThanTen(int n) => n > 10;
bool IsPrime(int n)
{
if (n < 2) return false;
for (int i = 2; i <= Math.Sqrt(n); i++)
if (n % i == 0) return false;
return true;
}
// متدی که از Delegate استفاده میکند
int[] FilterNumbers(int[] numbers, FilterFunction filter)
{
List<int> result = new List<int>();
foreach (int n in numbers)
{
if (filter(n))
result.Add(n);
}
return result.ToArray();
}
// استفاده
int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 };
int[] evens = FilterNumbers(nums, IsEven);
int[] greaterThanTen = FilterNumbers(nums, IsGreaterThanTen);
int[] primes = FilterNumbers(nums, IsPrime);
Console.WriteLine($"Evens: {string.Join(", ", evens)}");
Console.WriteLine($"Greater than 10: {string.Join(", ", greaterThanTen)}");
Console.WriteLine($"Primes: {string.Join(", ", primes)}");
---
⏱️ قسمت 3: Multicast Delegate (چند متدی)
یک Delegate میتواند به چندین متد اشاره کند.
csharp
delegate void PrintDelegate(string message);
void PrintToConsole(string msg)
{
Console.WriteLine($"Console: {msg}");
}
void PrintToFile(string msg)
{
Console.WriteLine($"File: [LOG] {msg}");
}
void PrintToDatabase(string msg)
{
Console.WriteLine($"Database: storing '{msg}'");
}
// استفاده از Multicast
PrintDelegate printer = PrintToConsole;
printer += PrintToFile; // اضافه کردن متد دوم
printer += PrintToDatabase; // اضافه کردن متد سوم
printer("Hello World!"); // هر سه متد اجرا میشوند
// حذف یک متد
printer -= PrintToFile;
Console.WriteLine("\nAfter removing PrintToFile:");
printer("Another message");
خروجی:
Console: Hello World!
File: [LOG] Hello World!
Database: storing 'Hello World!'
After removing PrintToFile:
Console: Another message
Database: storing 'Another message'
---
⏱️ قسمت 4: Delegateهای آماده (Func, Action, Predicate)
به جای تعریف Delegate خودمان، میتوانیم از اینها استفاده کنیم:
نوع ورودی خروجی کاربرد
Action 0 تا 16 پارامتر void بدون خروجی
Func 0 تا 16 پارامتر یک مقدار با خروجی
Predicate 1 پارامتر bool شرط
Action (بدون خروجی):
csharp
// Action بدون پارامتر
Action sayHello = () => Console.WriteLine("Hello!");
sayHello();
// Action با یک پارامتر
Action<string> greet = (name) => Console.WriteLine($"Hello {name}!");
greet("Ali");
// Action با دو پارامتر
Action<string, int> introduce = (name, age) =>
Console.WriteLine($"I'm {name}, {age} years old");
introduce("Sara", 25);
Func (با خروجی):
`csharp // Func با یک ورودی و یک خروجی Func<int, int> square = (x) => x * x; Console.WriteLine(square(5)); // 25
// Func با دو ورودی و یک خروجی
Func<int, int, int> add = (a, b) => a + b;
Console.WriteLine(add(10, 20)); // 30
// Func با string ورودی و bool خروجی
Func<string, bool> isEmpty = (s) => string.IsNullOrEmpty(s);
Console.WriteLine(isEmpty("")); // True
Console.WriteLine(isEmpty("Hi")); // False
Predicate (شرط):
csharp
// Predicate = Func<T, bool>
Predicate<int> isEven = (n) => n % 2 == 0;
Console.WriteLine(isEven(4)); // True
Console.WriteLine(isEven(5)); // False
// استفاده در List
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
List<int> evens = numbers.FindAll(isEven);
Console.WriteLine($"Evens: {string.Join(", ", evens)}");
---
⏱️ قسمت 5: Event چیست؟
Event = یک Delegate خاص که فقط در کلاس خودش صدا زده میشود.
csharp
class Button
{
// تعریف Delegate
public delegate void ClickHandler(object sender, EventArgs e);
// تعریف Event
public event ClickHandler Click;
// متد برای صدا زدن Event
public void OnClick()
{
Console.WriteLine("Button was clicked!");
Click?.Invoke(this, EventArgs.Empty); // اگر کسی مشترک شده باشد، صدا بزن
}
}
class Program
{
static void Main()
{
Button btn = new Button();
// اشتراک در رویداد
btn.Click += Button_Click;
btn.Click += Button_Click2;
// شبیهسازی کلیک
btn.OnClick();
}
static void Button_Click(object sender, EventArgs e)
{
Console.WriteLine("Event handler 1 executed");
}
static void Button_Click2(object sender, EventArgs e)
{
Console.WriteLine("Event handler 2 executed");
}
}
---
⏱️ قسمت 6: تفاوت Event با Delegate
ویژگی Delegate Event
قابل صدا زدن از بیرون ✅ ❌ (فقط داخل کلاس)
قابل اشتراک از بیرون ✅ ✅ (با +=)
قابل لغو اشتراک از بیرون ✅ ✅ (با -=)
قابل مقداردهی از بیرون ✅ (=) ❌ (فقط += و -=)
csharp
class Test
{
public delegate void MyDelegate(string msg);
public MyDelegate Del; // Delegate معمولی
public event MyDelegate Evt; // Event
public void TestMethod()
{
Del("From inside"); // ✅
Evt("From inside"); // ✅
}
}
// در بیرون
Test t = new Test();
t.Del = (msg) => Console.WriteLine(msg); // ✅
// t.Evt = (msg) => Console.WriteLine(msg); // ❌ خطا - نمیتوان =
t.Del += (msg) => Console.WriteLine(msg); // ✅
t.Evt += (msg) => Console.WriteLine(msg); // ✅
// t.Del("From outside"); // ✅
// t.Evt("From outside"); // ❌ خطا - نمیتوان از بیرون صدا زد
---
⏱️ قسمت 7: مثال واقعی – سیستم آب و هوا
csharp
using System;
using System.Collections.Generic;
// کلاس اطلاعات آب و هوا
class WeatherData
{
public double Temperature { get; set; }
public double Humidity { get; set; }
public string Condition { get; set; }
public override string ToString()
{
return $"{Condition}, {Temperature}°C, {Humidity}% humidity";
}
}
// کلاس ایستگاه آب و هوا (منتشرکننده رویداد)
class WeatherStation
{
// تعریف Delegate و Event
public delegate void WeatherUpdateHandler(object sender, WeatherData data);
public event WeatherUpdateHandler WeatherUpdated;
private WeatherData currentData;
public WeatherStation()
{
currentData = new WeatherData { Temperature = 25, Humidity = 60, Condition = "Sunny" };
}
public void UpdateWeather(double temp, double humidity, string condition)
{
currentData.Temperature = temp;
currentData.Humidity = humidity;
currentData.Condition = condition;
Console.WriteLine($"\n[WeatherStation] Weather updated: {currentData}");
// اطلاع به مشترکین
WeatherUpdated?.Invoke(this, currentData);
}
}
// کلاس نمایشگر (مشترک رویداد)
class DisplayScreen
{
private string name;
public DisplayScreen(string screenName)
{
name = screenName;
}
public void OnWeatherUpdated(object sender, WeatherData data)
{
Console.WriteLine($"[{name}] Displaying: {data}");
}
}
// کلاس هشداردهنده (مشترک دیگر)
class AlertSystem
{
public void OnWeatherUpdated(object sender, WeatherData data)
{
if (data.Temperature > 35)
Console.WriteLine($"[Alert] HEAT WARNING! {data.Temperature}°C");
else if (data.Temperature < 0)
Console.WriteLine($"[Alert] FREEZE WARNING! {data.Temperature}°C");
else if (data.Humidity > 80)
Console.WriteLine($"[Alert] HIGH HUMIDITY! {data.Humidity}%");
}
}
// برنامه اصلی
class Program
{
static void Main()
{
WeatherStation station = new WeatherStation();
DisplayScreen screen1 = new DisplayScreen("Living Room");
DisplayScreen screen2 = new DisplayScreen("Phone App");
AlertSystem alerts = new AlertSystem();
// اشتراک در رویداد
station.WeatherUpdated += screen1.OnWeatherUpdated;
station.WeatherUpdated += screen2.OnWeatherUpdated;
station.WeatherUpdated += alerts.OnWeatherUpdated;
// بهروزرسانی آب و هوا
station.UpdateWeather(28, 55, "Sunny");
station.UpdateWeather(38, 45, "Hot");
station.UpdateWeather(-2, 70, "Snowy");
station.UpdateWeather(30, 85, "Rainy");
Console.WriteLine("\nPress Enter to exit...");
Console.ReadLine();
}
}
---
✅ تمرین نهایی درس سیزدهم
برنامهای بنویسید که:
1. کلاس BankAccount با رویداد BalanceChanged
2. وقتی موجودی تغییر کرد، چندین مشترک مطلع شوند
3. از Action و Func در متدهای مختلف استفاده کنید
csharp
using System;
class BankAccount
{
private double balance;
// تعریف رویداد
public event Action<object, double, double> BalanceChanged; // sender, oldBalance, newBalance
public string AccountNumber { get; set; }
public string Owner { get; set; }
public double Balance
{
get { return balance; }
private set
{
double oldBalance = balance;
balance = value;
// صدا زدن رویداد
BalanceChanged?.Invoke(this, oldBalance, balance);
}
}
public BankAccount(string number, string owner, double initialBalance)
{
AccountNumber = number;
Owner = owner;
balance = initialBalance;
}
public void Deposit(double amount)
{
if (amount > 0)
{
Balance += amount;
Console.WriteLine($"Deposited: {amount:C}");
}
}
public void Withdraw(double amount)
{
if (amount > 0 && amount <= Balance)
{
Balance -= amount;
Console.WriteLine($"Withdrawn: {amount:C}");
}
else
{
Console.WriteLine("Insufficient balance or invalid amount!");
}
}
}
class Program
{
static void Main()
{
BankAccount account = new BankAccount("123456789", "Ali Rezaei", 1000000);
// مشترکین رویداد
account.BalanceChanged += OnBalanceChanged_Logger;
account.BalanceChanged += OnBalanceChanged_Email;
account.BalanceChanged += OnBalanceChanged_SMS;
// انجام عملیات
Console.WriteLine("=== Bank Account Transactions ===\n");
account.Deposit(500000);
account.Withdraw(200000);
account.Withdraw(1500000); // ناموفق
account.Deposit(1000000);
Console.WriteLine("\nPress Enter to exit...");
Console.ReadLine();
}
static void OnBalanceChanged_Logger(object sender, double oldBalance, double newBalance)
{
BankAccount acc = (BankAccount)sender;
Console.WriteLine($"[LOG] Account {acc.AccountNumber}: {oldBalance:C} → {newBalance:C}");
}
static void OnBalanceChanged_Email(object sender, double oldBalance, double newBalance)
{
BankAccount acc = (BankAccount)sender;
Console.WriteLine($"[EMAIL] Dear {acc.Owner}, your balance changed to {newBalance:C}");
}
static void OnBalanceChanged_SMS(object sender, double oldBalance, double newBalance)
{
Console.WriteLine($"[SMS] Balance update: {newBalance:C}");
}
}
`--- 📌 جمعبندی درس سیزدهم مفهوم توضیح مثال delegate تعریف نماینده delegate int MyDel(int x); Action Delegate بدون خروجی Action<string> print = s => Console.WriteLine(s); Func Delegate با خروجی Func<int,int> square = x => x*x; Predicate Delegate شرط Predicate<int> isEven = n => n%2==0; event رویداد (نسخه امن Delegate) public event Action MyEvent; کاربردهای اصلی: · Callback (فراخوانی بعد از انجام کار) · Event Handling (مدیریت رویدادها مانند کلیک) · LINQ (بسیاری از متدهای LINQ از Delegate استفاده میکنند) · ساختارهای انعطافپذیر --- 🧪 تمرین برای شما 1. یک Func بنویسید که دو string بگیرد و برگرداند طول بزرگترین آنها 2. یک Action بنویسید که یک آرایه بگیرد و همه اعضا را چاپ کند 3. یک کلاس Timer بسازید با رویداد Tick که هر ثانیه یک بار صدا زده شود 4. یک برنامه سفارش آنلاین بسازید که رویدادهای OrderPlaced، OrderShipped، OrderDelivered داشته باشد --- اگر این درس را کامل فهمیدید، بگویید تا درس چهاردهم (LINQ) را شروع کنیم. سوالی بود بپرسید.
خیلی خوب، پس شروع میکنیم درس چهاردهم – LINQ قسمت اول: آشنایی با LINQ و متدهای پایه.
یک پروژه جدید کنسولی بسازید (dotnet new console) و کدها را همراه من بنویسید و اجرا کنید.
---
📘 درس چهاردهم – LINQ قسمت 1: آشنایی با LINQ
🎯 سرفصلها:
1. LINQ چیست و چرا نیاز داریم؟
2. دو روش نوشتن LINQ (Query Syntax و Method Syntax)
3. متد Where (فیلتر کردن)
4. متد Select (انتخاب و تبدیل)
5. متدهای First، FirstOrDefault، Single، SingleOrDefault
6. متد ToList و اجرای دیررس (Deferred Execution)
7. تمرین جامع
---
⏱️ قسمت 1: LINQ چیست؟
LINQ = Language Integrated Query
زبانی برای پرس و جو از دادهها درون خود سیشارپ.
بدون LINQ (کد قدیمی و طولانی):
csharp
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
List<int> evens = new List<int>();
foreach (int n in numbers)
{
if (n % 2 == 0)
{
evens.Add(n);
}
}
با LINQ (کد کوتاه و خواناتر):
csharp
using System.Linq; // اضافه کردن این using لازم است
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var evens = numbers.Where(n => n % 2 == 0).ToList();
📌 نکته: حتماً using System.Linq; را در بالای فایل اضافه کنید.
---
⏱️ قسمت 2: دو روش نوشتن LINQ
روش اول: Method Syntax (روش متدی) – رایجتر
csharp
var result = numbers.Where(n => n > 5).Select(n => n * 2);
روش دوم: Query Syntax (روش شبه SQL) – شبیه به SQL
csharp
var result = from n in numbers
where n > 5
select n * 2;
مقایسه هر دو روش:
csharp
int[] scores = { 85, 92, 78, 90, 88, 70, 95 };
// Method Syntax
var highScores1 = scores.Where(s => s >= 90).OrderBy(s => s);
// Query Syntax
var highScores2 = from s in scores
where s >= 90
orderby s
select s;
Console.WriteLine("High scores (>=90):");
foreach (var score in highScores1)
{
Console.Write(score + " "); // 90 92 95
}
📌 نکته: هر دو روش نتیجه یکسان دارند. Method Syntax پرکاربردتر است.
---
⏱️ قسمت 3: متد Where (فیلتر کردن)
Where برای فیلتر کردن دادهها بر اساس یک شرط استفاده میشود.
csharp
List<string> names = new List<string>
{
"Ali", "Reza", "Sara", "Mohammad", "Neda", "Hossein"
};
// نامهایی که با "A" شروع میشوند
var startsWithA = names.Where(n => n.StartsWith("A"));
Console.WriteLine("Starts with A: " + string.Join(", ", startsWithA));
// نامهایی که طول آنها بیشتر از 4 است
var longNames = names.Where(n => n.Length > 4);
Console.WriteLine("Long names: " + string.Join(", ", longNames));
// ترکیب چند شرط
var result = names.Where(n => n.Length > 3 && n.Contains("e"));
Console.WriteLine("Long names with 'e': " + string.Join(", ", result));
---
⏱️ قسمت 4: متد Select (انتخاب و تبدیل)
Select برای تبدیل هر عنصر به شکل دیگری استفاده میشود.
csharp
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
// تبدیل هر عدد به مربع آن
var squares = numbers.Select(n => n * n);
Console.WriteLine("Squares: " + string.Join(", ", squares)); // 1,4,9,16,25
// تبدیل هر عدد به متن
var textNumbers = numbers.Select(n => $"Number: {n}");
foreach (var item in textNumbers)
{
Console.WriteLine(item);
}
مثال با کلاس Person:
csharp
class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string City { get; set; }
}
List<Person> people = new List<Person>
{
new Person { Name = "Ali", Age = 25, City = "Tehran" },
new Person { Name = "Sara", Age = 30, City = "Shiraz" },
new Person { Name = "Reza", Age = 22, City = "Tehran" },
new Person { Name = "Neda", Age = 28, City = "Isfahan" }
};
// فقط نامها را انتخاب کن
var namesOnly = people.Select(p => p.Name);
Console.WriteLine("Names: " + string.Join(", ", namesOnly));
// نام و سن را به صورت یک شیء ناشناس (Anonymous) انتخاب کن
var nameAndAge = people.Select(p => new { p.Name, p.Age });
foreach (var item in nameAndAge)
{
Console.WriteLine($"{item.Name} is {item.Age} years old");
}
---
⏱️ قسمت 5: ترکیب Where و Select
`csharp List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };