Lesson ১১৭ মিনিট পড়া

কালেকশনস এবং জেনেরিকস (Collections & Generics)

প্রতিটি কাজের জন্যই রয়েছে সঠিক পাত্র বা কন্টেইনার (container)

অ্যারে (Arrays) এবং লিস্টের (Lists) বাইরে (Beyond Arrays and Lists)

আপনি তো ইতিমধ্যেই List<T> এর ব্যাপারে জেনে গেছেন, কিন্তু সি শার্পে (C#) এর বাইরেও বিভিন্ন ধরনের কালেকশনের (collections) একটি বড় টুলবক্স (toolbox) রয়েছে, যার প্রতিটি টুল আলাদা আলাদা ধরনের কাজের জন্য তৈরি করা হয়েছে। সঠিক কালেকশনটি বেছে নেওয়া অনেকটা রান্নাঘরে সঠিক বোয়ম বা পাত্র বেছে নেওয়ার মতোই:

  • Dictionary<K,V> — একটি লেবেল করা ফাইলিং ক্যাবিনেট (filing cabinet)। কি (key) ব্যবহার করে মুহূর্তের মধ্যেই যেকোনো কিছু খুঁজে বের করা যায়।
  • HashSet<T> — এটি হলো ইউনিক বা অদ্বিতীয় আইটেমের একটি ব্যাগ। এখানে একই জিনিস দুইবার রাখা যায় না (No duplicates allowed)। "এই জিনিসটি কি এখানে আছে?" — এমন কিছু খুব দ্রুত খুঁজতে এটি ব্যবহৃত হয়।
  • Queue<T> — একটি দোকানের লাইন। যে সবার আগে আসবে, সে-ই সবার আগে সার্ভিস পাবে (First in, first out অথবা FIFO)।
  • Stack<T> — একটি প্লেটের স্তূপ (stack of plates)। যেই প্লেটটি সবার শেষে রাখা হয়েছে, সেটিকেই সবার আগে তুলে নেওয়া হবে (Last in, first out অথবা LIFO)।

এখানে <T> অংশটিকে একটি জেনেরিক টাইপ প্যারামিটার (generic type parameter) বলা হয়। এর মানে হলো "এর ভেতরে কী ধরনের জিনিস বা টাইপের (type) ডেটা থাকবে, তা আপনিই ঠিক করে দেবেন।" জেনেরিকস আপনাকে টাইপ সেফটি (type safety) দেয়, যার ফলে প্রতিটি টাইপের জন্য আলাদা করে নতুন কোনো কোড লিখতে হয় না।

Dictionary<K,V> — কি-ভ্যালু দিয়ে খুঁজে বের করা (Dictionary<K,V> — Key-Value Lookup)

// Create a dictionary: country code → country name
var countries = new Dictionary<string, string>
{
["US"] = "United States",
["JP"] = "Japan",
["BR"] = "Brazil"
};
// Add more entries
countries["DE"] = "Germany";
countries["IN"] = "India";
// Look up by key
Console.WriteLine($"JP = {countries["JP"]}");
// Safe lookup (avoid KeyNotFoundException)
if (countries.TryGetValue("FR", out string? name))
Console.WriteLine($"FR = {name}");
else
Console.WriteLine("FR not found!");
// Check if key exists
Console.WriteLine($"Has US? {countries.ContainsKey("US")}");
// Iterate over all pairs
Console.WriteLine($"\nAll {countries.Count} countries:");
foreach (var pair in countries)
Console.WriteLine($" {pair.Key}{pair.Value}");
Output
JP = Japan
FR not found!
Has US? True

All 5 countries:
  US → United States
  JP → Japan
  BR → Brazil
  DE → Germany
  IN → India

HashSet<T> — কেবল ইউনিক বা অদ্বিতীয় আইটেমের জন্য (HashSet<T> — Unique Items Only)

var visited = new HashSet<string> { "Paris", "Tokyo", "London" };
// Add — returns false if already exists
bool added1 = visited.Add("Sydney"); // true — new city
bool added2 = visited.Add("Paris"); // false — already there!
Console.WriteLine($"Added Sydney: {added1}");
Console.WriteLine($"Added Paris again: {added2}");
// Super fast contains check
Console.WriteLine($"Been to Tokyo? {visited.Contains("Tokyo")}");
// Set operations
var wishlist = new HashSet<string> { "Rome", "Tokyo", "Cairo" };
// Intersection — cities in BOTH sets
var both = new HashSet<string>(visited);
both.IntersectWith(wishlist);
Console.Write("In both: ");
foreach (var city in both) Console.Write($"{city} ");
Console.WriteLine();
// Except — in wishlist but not visited
wishlist.ExceptWith(visited);
Console.Write("Still need to visit: ");
foreach (var city in wishlist) Console.Write($"{city} ");
Console.WriteLine();
Output
Added Sydney: True
Added Paris again: False
Been to Tokyo? True
In both: Tokyo 
Still need to visit: Rome Cairo 

কিউ (Queue) ও স্ট্যাক (Stack) — এগুলোতে ক্রম বা অর্ডার (Order) খুবই গুরুত্বপূর্ণ (Queue & Stack — Order Matters)

Queue<T> এবং Stack<T> মূলত জিনিসপত্রগুলো কোন ক্রমে বের হবে, তা নিয়ন্ত্রণ করে:

  • কিউ বা Queue (FIFO): এটি অনেকটা সিনেমা হলের লাইনের মতো। লাইনে যিনি সবার আগে দাঁড়াবেন, তিনিই সবার আগে টিকিট পাবেন। নতুন কাউকে লাইনে যুক্ত করার জন্য Enqueue() এবং লাইন থেকে কাউকে বের করার জন্য Dequeue() ব্যবহার করুন।
  • স্ট্যাক বা Stack (LIFO): এটি প্যানকেকের একটি স্তুপের মতো। যে প্যানকেকটি সবার শেষে বানানো হয়েছে, সেটিই সবার আগে খাওয়া হবে। নতুন কিছু যুক্ত করতে Push() এবং কিছু সরিয়ে ফেলতে Pop() ব্যবহার করুন।

এই দুটির ক্ষেত্রেই একটি Peek() নামের মেথড রয়েছে, যা দিয়ে আপনি জিনিসটিকে বের না করেই দেখতে পারবেন যে পরবর্তী আইটেমটি কী।

Queue<T> এবং Stack<T>

// Queue — first in, first out
var printQueue = new Queue<string>();
printQueue.Enqueue("Report.pdf");
printQueue.Enqueue("Photo.jpg");
printQueue.Enqueue("Resume.docx");
Console.WriteLine($"Next to print: {printQueue.Peek()}");
Console.WriteLine($"Queue size: {printQueue.Count}");
while (printQueue.Count > 0)
{
string job = printQueue.Dequeue();
Console.WriteLine($" Printing: {job}");
}
// Stack — last in, first out
var undoStack = new Stack<string>();
undoStack.Push("Typed 'Hello'");
undoStack.Push("Made text bold");
undoStack.Push("Changed font to Arial");
Console.WriteLine($"\nLast action: {undoStack.Peek()}");
Console.WriteLine("Undoing:");
while (undoStack.Count > 0)
{
string action = undoStack.Pop();
Console.WriteLine($" Undo: {action}");
}
Output
Next to print: Report.pdf
Queue size: 3
  Printing: Report.pdf
  Printing: Photo.jpg
  Printing: Resume.docx

Last action: Changed font to Arial
Undoing:
  Undo: Changed font to Arial
  Undo: Made text bold
  Undo: Typed 'Hello'

নিজেদের জন্য জেনেরিক কোড লেখা (Writing Your Own Generic Code)

আপনি চাইলে নিজেই নিজের জেনেরিক ক্লাস (generic classes) এবং মেথড (methods) তৈরি করতে পারেন। <T> হলো একটি প্লেসহোল্ডার (placeholder), অর্থাৎ যখন কেউ আপনার কোড ব্যবহার করবে, তখন এটি একটি আসল টাইপ (real type) দিয়ে বদলে যাবে। এটি অনেকটা এমন কোনো রেসিপির মতো যা বলে, "যেকোনো T টাইপের একটি জিনিস নাও" — আর বাবুর্চি নিজেই ঠিক করেন যে সেই T আসলে কী হবে।

জেনেরিক মেথডস এবং ক্লাসেস (Generic Methods & Classes)

// Generic method — works with any type
static T GetFirst<T>(List<T> items)
{
if (items.Count == 0)
throw new InvalidOperationException("List is empty!");
return items[0];
}
// Generic class — a simple wrapper
class Pair<T1, T2>
{
public T1 First { get; }
public T2 Second { get; }
public Pair(T1 first, T2 second)
{
First = first;
Second = second;
}
public override string ToString() => $"({First}, {Second})";
}
// Using the generic method
var names = new List<string> { "Anika", "Rafi", "Sadia" };
var numbers = new List<int> { 42, 17, 99 };
Console.WriteLine($"First name: {GetFirst(names)}");
Console.WriteLine($"First number: {GetFirst(numbers)}");
// Using the generic class
var coordinate = new Pair<double, double>(40.7128, -74.0060);
var entry = new Pair<string, int>("Score", 100);
Console.WriteLine($"NYC: {coordinate}");
Console.WriteLine($"Entry: {entry}");
Output
First name: Anika
First number: 42
NYC: (40.7128, -74.006)
Entry: (Score, 100)
Note: 🗂️ কালেকশনের চিট শিট (Collection cheat sheet): কি (key) দিয়ে কিছু খুঁজতে হবে? → Dictionary ব্যবহার করুন। কোনো জিনিসের পুনরাবৃত্তি বা ডুপ্লিকেট (duplicates) চান না? → HashSet ব্যবহার করুন। FIFO (First In First Out) এর ক্রম (order) দরকার? → Queue ব্যবহার করুন। LIFO (Last In First Out) বা আনডু (undo) করার ক্ষমতা দরকার? → Stack ব্যবহার করুন। একটি নমনীয় এবং সাজানো লিস্ট দরকার? → List ব্যবহার করুন। সঠিক কালেকশনটি বেছে নিতে পারলেই আপনার অর্ধেক কাজ সহজ হয়ে যাবে!
চ্যালেঞ্জ

ছোট কুইজ

যদি আপনি একটি HashSet-এ এমন কোনো ভ্যালু যোগ করার চেষ্টা করেন, যা সেখানে আগে থেকেই আছে (duplicate value), তবে কী ঘটবে?

পড়া চালিয়ে যান

Interfaces & Abstract ClassesLINQ