C# క్లాసెస్, ఆబ్జెక్ట్స్, కన్స్ట్రక్టర్స్ & Modern Primary Constructors (C# 12)
స్వాగతం Phase 04 కు! 🏛️ ఆబ్జెక్ట్ ఓరియెంటెడ్ ప్రోగ్రామింగ్ (OOP) ద్వారా సాఫ్ట్వేర్ ఆర్కిటెక్చర్ నిర్మాణం!
ఇప్పటివరకు మనం వేరియబుల్స్, కంట్రోల్ ఫ్లో, మెథడ్స్, అర్రేలు మరియు కలెక్షన్స్ వంటి ప్రోగ్రామింగ్ ప్రాథమిక ఇటుకలను నేర్చుకున్నాం. కానీ వాస్తవ ప్రపంచంలోని బ్యాంకింగ్ ప్లాట్ఫామ్లు, ఈ-కామర్స్ పోర్టల్స్, గేమ్ ఇంజిన్లు మరియు క్లౌడ్ మైక్రోసర్వీసెస్ వంటి భారీ వ్యవస్థలను నిర్మించాలంటే — డేటా మరియు ఆ డేటాపై పనిచేసే పనులను విడివిడిగా ఉంచడం అసాధ్యం.
ఈ సమస్యను శాశ్వతంగా పరిష్కరించే ఆధునిక సాఫ్ట్వేర్ పద్ధతే Object-Oriented Programming (OOP). వాస్తవ ప్రపంచంలోని బ్యాంక్ అకౌంట్, కస్టమర్, షాపింగ్ కార్ట్ లేదా గేమ్ క్యారెక్టర్ను కంప్యూటర్ మెమరీలో ఒక సమగ్ర రూపంగా ఎలా మలచాలో ఈ అధ్యాయంలో నేర్చుకుంటాం. ముఖ్యంగా C# 12 ప్రవేశపెట్టిన శక్తివంతమైన "Primary Constructors" ద్వారా కోడ్ పొడవును 70% తగ్గించి, అత్యంత క్లీన్ అండ్ ఎలిగెంట్ ఆర్కిటెక్చర్ను ఎలా సృష్టించాలో ప్రాక్టికల్గా శోధిద్దాం!
1 🌍 ప్రొసీజరల్ నుండి ఆబ్జెక్ట్ ఓరియెంటెడ్ ప్రోగ్రామింగ్ (OOP) వైపు ప్రయాణం
ప్రారంభ దశలో కంప్యూటర్ ప్రోగ్రామ్లు ప్రొసీజరల్ (Procedural) లేదా స్ట్రక్చర్డ్ శైలిలో ఉండేవి. అంటే ప్రోగ్రామ్లో డేటా వేరుగా (గ్లోబల్ వేరియబుల్స్ లేదా స్ట్రక్చర్లలో), ఆ డేటాపై గణనలు చేసే ఫంక్షన్లు వేరుగా ఉండేవి.
ఈ విధానంలో సాఫ్ట్వేర్ పరిమాణం పెరిగేకొద్దీ మూడు తీవ్రమైన సమస్యలు తలెత్తుతాయి:
- డేటా ప్రొటెక్షన్ లోపించడం: ప్రోగ్రామ్లోని ఏ ఫంక్షన్ అయినా బ్యాంక్ బ్యాలెన్స్ను నేరుగా మార్చేయవచ్చు (ఉదా:
balance = -50000;). ఎటువంటి వాలిడేషన్ ఉండదు. - మెయింటెనెన్స్ నరకం: డేటా స్ట్రక్చర్లో ఒక్క వేరియబుల్ పేరు మారినా, వందలాది విడి ఫంక్షన్లను మాన్యువల్గా సవరించాల్సి వస్తుంది.
- రియల్-వరల్డ్ మోడలింగ్ అసమర్థత: వాస్తవ ప్రపంచంలో వస్తువులు కేవలం డేటా మాత్రమే కాదు; వాటికి ఒక స్థితి (State) మరియు ప్రవర్తన (Behavior) రెండూ కలిసి ఉంటాయి.
ఈ లోపాలను అధిగమించడానికి ఆబ్జెక్ట్ ఓరియెంటెడ్ ప్రోగ్రామింగ్ (OOP) ఆవిర్భవించింది. OOP లో ప్రతి ఎంటిటీ తన డేటాను మరియు ఆ డేటాపై పనిచేసే ఫంక్షన్లను ఒకే రక్షణ కవచం లోపల బంధిస్తుంది. ఇందులో నాలుగు ప్రధాన స్తంభాలు ఉంటాయి: Encapsulation, Abstraction, Inheritance, Polymorphism. ఈ 4వ ఫేజ్లో వీటన్నింటినీ లోతుగా ప్రాక్టికల్ కోడింగ్తో నేర్చుకుంటాం.
2 📐 క్లాస్ (Class) vs ఆబ్జెక్ట్ (Object) — బ్లూప్రింట్ vs ఇన్స్టాన్స్
OOP ను అర్థం చేసుకోవడానికి ప్రపంచ ప్రసిద్ధమైన రూపకం (Analogy) — ఆర్కిటెక్చర్ బ్లూప్రింట్ మరియు నిజమైన భవనం:
🏠 రియల్-వరల్డ్ అనాలజీ:
క్లాస్ (Class): ఒక సివిల్ ఇంజనీర్ కాగితంపై గీసిన ఇంటి ప్లాన్ (Blueprint). ప్లాన్ కాగితంపై ఉంటుంది కానీ మీరు అందులో నివసించలేరు. అది భూమిపై స్థలాన్ని (RAM) ఆక్రమించదు. అది కేవలం ఇల్లు ఎలా ఉండాలో సూచించే నియమావళి.
ఆబ్జెక్ట్ (Object / Instance): ఆ బ్లూప్రింట్ను చూసి నిజమైన ఇటుకలు, సిమెంట్తో కట్టిన వాస్తవ ఇల్లు. ఇది భూమిపై నిజంగా స్థలాన్ని (RAM Heap Memory) ఆక్రమిస్తుంది. ఒకే బ్లూప్రింట్ ఆధారంగా మీరు వందలాది వేర్వేరు రంగులు, ఫర్నిచర్ కలిగిన ఇళ్లను నిర్మించవచ్చు!
సాంకేతిక పరిభాషలో:
- Class: యూజర్ డిఫైన్డ్ బ్లూప్రింట్ లేదా డేటా టైప్ (User-Defined Reference Type). ఇది ఆబ్జెక్ట్కు ఏయే ఫీల్డ్స్ మరియు మెథడ్స్ ఉండాలో నిర్వచిస్తుంది.
- Object: క్లాస్ యొక్క ప్రత్యక్ష ప్రతిరూపం (Concrete Instance of a Class). ఇది రన్టైమ్లో మెమరీని కలిగి ఉంటుంది.
// 1. క్లాస్ డిక్లరేషన్ (బ్లూప్రింట్)
public class Car
{
// ఫీల్డ్స్ (State / Data)
public string Model;
public string Color;
public int Speed;
// మెథడ్స్ (Behavior / Functions)
public void Accelerate(int increase)
{
Speed += increase;
Console.WriteLine($"{Model} వేగం పెరిగింది: {Speed} km/h");
}
public void ApplyBrakes()
{
Speed = 0;
Console.WriteLine($"{Model} ఆగింది!");
}
}
// 2. ఆబ్జెక్ట్స్ క్రియేట్ చేయడం (Instances)
Car car1 = new Car();
car1.Model = "Tesla Model 3";
car1.Color = "Red";
car1.Accelerate(45);
Car car2 = new Car();
car2.Model = "Tata Harrier";
car2.Color = "Dark Edition";
car2.Accelerate(60);
3 🧠 `new` కీవర్డ్ & Managed Heap మెమరీ ఆర్కిటెక్చర్
C# లో ఒక క్లాస్ ఎల్లప్పుడూ రిఫరెన్స్ టైప్ (Reference Type). కింది కోడ్ లైన్ రన్ అయినప్పుడు కంప్యూటర్ మెమరీలో అంతర్గతంగా ఏం జరుగుతుందో తెలుసుకోవడం సీనియర్ ఇంజనీర్గా మారడానికి అత్యంత కీలకం:
ఈ ఒక్క సింగిల్ స్టేట్మెంట్లో 3 అద్భుతమైన మెమరీ దశలు జరుగుతాయి:
| దశ (Phase) | కోడ్ భాగం | మెమరీ ప్రాంతం | అంతర్గతంగా ఏం జరుగుతుంది? |
|---|---|---|---|
| 1. Reference Variable | Car myCar |
Stack Memory | స్టాక్లో 64-బిట్ (8 బైట్లు) లేదా 32-బిట్ మెమరీ అలోకేట్ అవుతుంది. ప్రారంభంలో దీని విలువ null. ఇది ఆబ్జెక్ట్ యొక్క అడ్రస్ను దాచడానికి సిద్ధంగా ఉన్న పాయింటర్. |
| 2. Memory Allocation | new |
Managed Heap | CLR (Common Language Runtime) మేనేజ్డ్ హీప్లో Car క్లాస్కు అవసరమైన బైట్ల మెమరీని రిజర్వ్ చేస్తుంది. అదనంగా Object Header (Sync Block Index, Type Handle) కోసం 16 బైట్ల ఓవర్హెడ్ చేరుతుంది. |
| 3. Initialization & Binding | Car() మరియు = |
Stack & Heap Link | కన్స్ట్రక్టర్ రన్ అయి డీఫాల్ట్ విలువలను సెట్ చేస్తుంది. హీప్లో ఆబ్జెక్ట్ క్రియేట్ అయిన మెమరీ అడ్రస్ (ఉదా: 0x7FFF0A2B) స్టాక్లోని myCar వేరియబుల్కు అసైన్ అవుతుంది. |
ఇప్పుడు మీరు Car anotherCar = myCar; అని రాస్తే, హీప్లోని ఆబ్జెక్ట్ డూప్లికేట్ అవ్వదు!
కేవలం ఆబ్జెక్ట్ యొక్క మెమరీ అడ్రస్ మాత్రమే కాపీ అవుతుంది. అంటే myCar మరియు anotherCar రెండూ హీప్లోని ఒకే ఒక్క ఆబ్జెక్ట్ను పాయింట్ చేస్తాయి.
ఒక వేరియబుల్ ద్వారా డేటాను మార్చినా, రెండో వేరియబుల్లో ఆ మార్పు వెంటనే కనిపిస్తుంది!
4 🏷️ ఫీల్డ్స్ (Fields) & మెథడ్స్ (Methods) డిక్లరేషన్
ఒక క్లాస్ లోపల ఉండే వేరియబుల్స్ను ఫీల్డ్స్ (Fields) లేదా మెంబర్ వేరియబుల్స్ అంటారు. లోకల్ వేరియబుల్స్తో పోలిస్తే ఫీల్డ్స్కు ఒక విశేషమైన లక్షణం ఉంది: మనం ఎటువంటి విలువ ఇవ్వకపోయినా, C# వాటికి డీఫాల్ట్ విలువలను (Default Values) ఆటోమేటిక్గా కేటాయిస్తుంది.
| డేటా టైప్ | ఆటోమేటిక్ డీఫాల్ట్ విలువ | ఉదాహరణ ఫీల్డ్ |
|---|---|---|
int, short, byte, long |
0 |
public int AccountNumber; // 0 |
float, double, decimal |
0.0f, 0.0d, 0.0m |
public decimal Balance; // 0.0m |
bool |
false |
public bool IsActive; // false |
char |
'\0' (Null character) |
public char Grade; // '\0' |
string లేదా ఏ Reference Type అయినా |
null |
public string AccountHolder; // null |
public class UserAccount
{
// ఫీల్డ్స్ - మనం వేల్యూ ఇవ్వకున్నా డీఫాల్ట్ వాల్యూస్ వస్తాయి
public string Username;
public int LoginAttempts;
public bool IsLocked;
// ఇన్స్టాన్స్ మెథడ్ - ఫీల్డ్స్ పై పనిచేస్తుంది
public void DisplayStatus()
{
Console.WriteLine($"యూజర్: {Username ?? "అనామకుడు"}");
Console.WriteLine($"లాగిన్ ప్రయత్నాలు: {LoginAttempts}");
Console.WriteLine($"ఖాతా లాక్ అయిందా?: {(IsLocked ? "అవును" : "కాదు")}");
}
}
UserAccount user = new UserAccount();
user.DisplayStatus();
// అవుట్పుట్: యూజర్: అనామకుడు, లాగిన్ ప్రయత్నాలు: 0, ఖాతా లాక్ అయిందా?: కాదు
5 🏗️ కన్స్ట్రక్టర్ అంటే ఏమిటి? డీఫాల్ట్ కన్స్ట్రక్టర్ (Default Constructor)
ఒక ఆబ్జెక్ట్ మెమరీలో పుట్టే క్షణంలో (Instantiation టైమ్లో) ఆటోమేటిక్గా ఎగ్జిక్యూట్ అయ్యే ప్రత్యేకమైన మెథడ్ను కన్స్ట్రక్టర్ (Constructor) అంటారు.
కన్స్ట్రక్టర్ యొక్క 3 బంగారు నిబంధనలు:
- కన్స్ట్రక్టర్ పేరు ఖచ్చితంగా క్లాస్ పేరుతో సమానంగా ఉండాలి (Case-sensitive).
- కన్స్ట్రక్టర్కు ఎటువంటి రిటర్న్ టైప్ ఉండకూడదు — కనీసం
voidకూడా ఉండకూడదు! - మనం క్లాస్లో ఒక్క కన్స్ట్రక్టర్ కూడా రాయకపోతే, C# కంపైలర్ స్వయంగా ఎటువంటి పారామీటర్లు లేని ఒక డీఫాల్ట్ కన్స్ట్రక్టర్ (Default Parameterless Constructor) ను కోడ్లోకి ఇంజెక్ట్ చేస్తుంది.
public class ServerNode
{
public string NodeId;
public string Region;
public DateTime StartedAt;
// మనం స్పష్టంగా రాసిన పారామీటర్ల లేని డీఫాల్ట్ కన్స్ట్రక్టర్
public ServerNode()
{
NodeId = Guid.NewGuid().ToString().Substring(0, 8);
Region = "ap-south-1 (Hyderabad)";
StartedAt = DateTime.UtcNow;
Console.WriteLine($"⚡ క్లౌడ్ సర్వర్ నోడ్ ప్రారంభమైంది: {NodeId} [{Region}]");
}
}
// ఆబ్జెక్ట్ క్రియేట్ చేయగానే కన్స్ట్రక్టర్ రన్ అవుతుంది
ServerNode s1 = new ServerNode();
ServerNode s2 = new ServerNode();
6 🎯 పారామీటరైజ్డ్ కన్స్ట్రక్టర్స్ (Parameterized Constructors) & ఓవర్లోడింగ్
రియల్-వరల్డ్ ప్రోగ్రామింగ్లో ఆబ్జెక్ట్ పుట్టేప్పుడే దానికి అవసరమైన అసలైన విలువలను (Data Integrity) అందించాలి. ఉదాహరణకు ఒక ఎంప్లాయ్ ఆబ్జెక్ట్ క్రియేట్ చేసేటప్పుడు ఖచ్చితంగా ID, పేరు, జీతం ఉండాలి గానీ ఖాళీగా ఉండకూడదు.
బయటి నుండి విలువలను స్వీకరించి ఫీల్డ్స్ను ఇనిషియలైజ్ చేసే కన్స్ట్రక్టర్లను పారామీటరైజ్డ్ కన్స్ట్రక్టర్స్ అంటారు. వివిధ పారామీటర్లతో బహుళ కన్స్ట్రక్టర్లను రాయడాన్ని Constructor Overloading అంటారు.
new ClassName() కూడా కావాలంటే, మీరు స్వయంగా public ClassName() { } రాయాల్సిందే!
public class Employee
{
public int Id;
public string Name;
public decimal MonthlySalary;
public string Department;
// 1. డీఫాల్ట్ కన్స్ట్రక్టర్
public Employee()
{
Id = 0;
Name = "నూతన అభ్యర్థి";
MonthlySalary = 25000m;
Department = "ట్రైనీ";
}
// 2. 2 పారామీటర్ల కన్స్ట్రక్టర్
public Employee(int id, string name)
{
Id = id;
Name = name;
MonthlySalary = 50000m; // డీఫాల్ట్ బేస్ శాలరీ
Department = "జనరల్";
}
// 3. పూర్తి 4 పారామీటర్ల కన్స్ట్రక్టర్
public Employee(int id, string name, decimal salary, string dept)
{
Id = id;
Name = name;
MonthlySalary = salary;
Department = dept;
}
}
// మూడు వేర్వేరు కన్స్ట్రక్టర్లను పిలవడం
Employee e1 = new Employee();
Employee e2 = new Employee(101, "రాజేష్ వర్మ");
Employee e3 = new Employee(102, "స్నేహలత", 95000m, "క్లౌడ్ ఆర్కిటెక్చర్");
7 🔗 `this` కీవర్డ్ & Constructor Chaining (`: this(...)`)
C# లో this అనేది ప్రస్తుతం ఎగ్జిక్యూట్ అవుతున్న ఆబ్జెక్ట్ ఇన్స్టాన్స్ను సూచించే ఒక హిడెన్ రిఫరెన్స్ వేరియబుల్.
దీనికి రెండు ప్రధాన ఉపయోగాలు ఉన్నాయి:
- షాడోయింగ్ (Shadowing) నివారణ: కన్స్ట్రక్టర్ పారామీటర్ పేరు మరియు క్లాస్ ఫీల్డ్ పేరు ఒకటే అయినప్పుడు, కంప్యూటర్కు గందరగోళం లేకుండా
this.name = name;అని ఫీల్డ్ను వేరు చేసి చెప్పడానికి వాడతాం. - Constructor Chaining (కన్స్ట్రక్టర్ చైనింగ్): పై సెక్షన్లో చూసినట్లు ప్రతి కన్స్ట్రక్టర్లో మళ్లీ మళ్లీ అదే అసైన్మెంట్ కోడ్ రాయడం DRY ప్రిన్సిపల్కు విరుద్ధం. ఒక కన్స్ట్రక్టర్ నుండి ఇంకో కన్స్ట్రక్టర్ను
: this(...)సింటాక్స్ ద్వారా కాల్ చేయవచ్చు.
public class Product
{
public string Sku;
public string Title;
public decimal Price;
public int StockQuantity;
// ప్రధాన మాస్టర్ కన్స్ట్రక్టర్ - ఇక్కడే అసలైన ఇనిషియలైజేషన్ జరుగుతుంది
public Product(string sku, string title, decimal price, int stock)
{
this.Sku = sku;
this.Title = title;
this.Price = price;
this.StockQuantity = stock;
}
// 2 పారామీటర్ల కన్స్ట్రక్టర్ -> మాస్టర్ కన్స్ట్రక్టర్కు రీడైరెక్ట్ చేస్తుంది!
public Product(string sku, string title) : this(sku, title, 99.00m, 10)
{
Console.WriteLine($"స్టాక్ డిఫాల్ట్గా 10 కి సెట్ చేయబడింది.");
}
// 1 పారామీటర్ కన్స్ట్రక్టర్ -> 2 పారామీటర్ల కన్స్ట్రక్టర్కు చైన్ అవుతుంది!
public Product(string sku) : this(sku, "పేరులేని ఉత్పత్తి")
{
}
}
// ఒకే లైన్ లో చైనింగ్ ద్వారా సురక్షితమైన ఆబ్జెక్ట్ రెడీ
Product p = new Product("SKU-9021");
Console.WriteLine($"వస్తువు: {p.Title}, ధర: ₹{p.Price}, స్టాక్: {p.StockQuantity}");
// అవుట్పుట్: వస్తువు: పేరులేని ఉత్పత్తి, ధర: ₹99.00, స్టాక్: 10
8 🚀 C# 12 విప్లవాత్మక ఫీచర్ — Primary Constructors in Classes
C# 9 లో రికార్డ్స్ (Records) లో ప్రవేశపెట్టిన ప్రైమరీ కన్స్ట్రక్టర్ల విజయాన్ని చూసి, మైక్రోసాఫ్ట్ C# 12 (.NET 8) లో సాధారణ క్లాసులకు కూడా Primary Constructors ని తీసుకొచ్చింది! ఇది ఆధునిక C# చరిత్రలోనే అత్యంత ప్రజాదరణ పొందిన ఫీచర్లలో ఒకటి.
⚡ పాత C# vs C# 12 కోడ్ పోలిక:
public class User {
private string _name;
private int _age;
public User(string name, int age) {
_name = name;
_age = age;
}
}
// క్లాస్ పక్కనే డైరెక్ట్ పారామీటర్లు!
public class User(string name, int age)
{
public void Print() =>
Console.WriteLine($"{name}: {age}");
}
Primary Constructors ముఖ్యాంశాలు & నియమాలు:
- ప్రైమరీ కన్స్ట్రక్టర్ పారామీటర్లు క్లాస్ మొత్తం పరిధిలో (Scope) అందుబాటులో ఉంటాయి. వాటిని మెథడ్స్లో, ప్రాపర్టీ ఇనిషియలైజర్లలో నేరుగా వాడవచ్చు.
- మీరు క్లాస్లో అదనంగా ఇతర కన్స్ట్రక్టర్లను (Secondary Constructors) రాయాలనుకుంటే, అవి తప్పనిసరిగా ప్రైమరీ కన్స్ట్రక్టర్ను
: this(...)తో కాల్ చేయాలి. - డిపెండెన్సీ ఇంజెక్షన్ (Dependency Injection - DI) లో సర్వీసులను ఇంజెక్ట్ చేసుకోవడానికి ఇది ASP.NET Core మైక్రోసర్వీసెస్లో విపరీతంగా వాడుతున్నారు.
// C# 12 Primary Constructor డిక్లరేషన్
public class BankClient(string fullName, string panNumber, decimal openingBalance)
{
// పారామీటర్ను నేరుగా ప్రాపర్టీకి కేటాయించవచ్చు
public string ClientName { get; } = fullName;
public string Pan { get; } = panNumber.ToUpper();
public decimal Balance { get; private set; } = openingBalance;
// సెకండరీ కన్స్ట్రక్టర్ తప్పనిసరిగా ప్రైమరీ కన్స్ట్రక్టర్ను చైన్ చేయాలి
public BankClient(string fullName, string panNumber)
: this(fullName, panNumber, 1000.00m) // కనీస ప్రారంభ బ్యాలెన్స్
{
}
public void Deposit(decimal amount)
{
Balance += amount;
Console.WriteLine($"₹{amount:N2} జమ అయ్యింది. కస్టమర్: {fullName}, కొత్త బ్యాలెన్స్: ₹{Balance:N2}");
}
}
var client = new BankClient("సునీత రావు", "ABCDE1234F");
client.Deposit(5000m);
9 🎨 ఆబ్జెక్ట్ ఇనిషియలైజర్ సింటాక్స్ (Object Initializer Syntax)
ఒక క్లాస్కు పదుల సంఖ్యలో ఫీల్డ్స్ లేదా ప్రాపర్టీలు ఉన్నప్పుడు, ప్రతి కాంబినేషన్కు కన్స్ట్రక్టర్లను రాసుకుంటూ వెళ్తే కన్స్ట్రక్టర్ల సంఖ్య పెరిగిపోతుంది. దీనికి పరిష్కారంగా C# అందించిన సులభమైన ఫీచర్ Object Initializers.
కన్స్ట్రక్టర్ పక్కనే కర్లీ బ్రేసెస్ { } ఉపయోగించి ఫీల్డ్స్ లేదా ప్రాపర్టీలకు నేరుగా విలువలను కేటాయించవచ్చు:
public class Laptop
{
public string Brand { get; set; }
public string Processor { get; set; }
public int RamGb { get; set; }
public decimal Price { get; set; }
}
// 1. ఆబ్జెక్ట్ ఇనిషియలైజర్ తో సులభంగా సృష్టించడం
Laptop myLaptop = new Laptop
{
Brand = "Dell XPS 15",
Processor = "Intel Core i9 14th Gen",
RamGb = 32,
Price = 185000.00m
};
// 2. లిస్ట్ కలెక్షన్తో కలిపి వాడటం (Collection Initializer)
List inventory = new()
{
new Laptop { Brand = "Apple MacBook Pro", Processor = "M3 Max", RamGb = 64, Price = 340000m },
new Laptop { Brand = "Lenovo Legion", Processor = "AMD Ryzen 9", RamGb = 16, Price = 135000m }
};
Console.WriteLine($"ఇన్వెంటరీలో ల్యాప్టాప్లు: {inventory.Count}");
10 ⚡ స్టాటిక్ మెంబర్స్ vs ఇన్స్టాన్స్ మెంబర్స్ (`static` Fields, Methods, Constructors)
C# లో ఒక మెంబర్ ముందు static అని రాస్తే, అది ఏ ఒక్క నిర్దిష్ట ఆబ్జెక్ట్కు కాకుండా —
మొత్తం క్లాస్కు (Type-level) చెందుతుంది.
| ఫీచర్ | ఇన్స్టాన్స్ మెంబర్ (Non-Static) | స్టాటిక్ మెంబర్ (Static) |
|---|---|---|
| మెమరీ కేటాయింపు | ప్రతి ఆబ్జెక్ట్ క్రియేట్ అయినప్పుడు కొత్తగా Heap లో కేటాయించబడుతుంది. | అప్లికేషన్ లైఫ్టైమ్లో AppDomain లో ఒకే ఒక్కసారి మెమరీ షేర్ అవుతుంది. |
| యాక్సెస్ చేసే విధానం | ఆబ్జెక్ట్ వేరియబుల్ ద్వారా: myCar.Speed |
క్లాస్ పేరు ద్వారా నేరుగా: Car.TotalCarsSold |
| కన్స్ట్రక్టర్ | ప్రతి new ఆపరేటర్ అప్పుడు రన్ అవుతుంది. |
క్లాస్ మొదటిసారి మెమరీలోకి లోడ్ అయినప్పుడు ఒకే ఒక్కసారి రన్ అవుతుంది. పారామీటర్లు ఉండవు! |
public class DatabaseConnection
{
// ఇన్స్టాన్స్ ఫీల్డ్ (ప్రతి కనెక్షన్కు వేరుగా ఉంటుంది)
public string ConnectionId { get; set; }
// స్టాటిక్ ఫీల్డ్ (అన్ని కనెక్షన్లు షేర్ చేసుకునే గ్లోబల్ కౌంటర్)
public static int ActiveConnectionCount { get; private set; }
// స్టాటిక్ కన్స్ట్రక్టర్ - జీవితంలో ఒక్కసారే రన్ అవుతుంది
static DatabaseConnection()
{
ActiveConnectionCount = 0;
Console.WriteLine("🌐 గ్లోబల్ డేటాబేస్ పూల్ ఇనిషియలైజ్ చేయబడింది.");
}
// ఇన్స్టాన్స్ కన్స్ట్రక్టర్
public DatabaseConnection(string id)
{
ConnectionId = id;
ActiveConnectionCount++; // స్టాటిక్ కౌంటర్ పెంపు
}
// స్టాటిక్ మెథడ్
public static void PrintGlobalPoolStatus()
{
Console.WriteLine($"ప్రస్తుతం యాక్టివ్గా ఉన్న డేటాబేస్ కనెక్షన్లు: {ActiveConnectionCount}");
}
}
DatabaseConnection c1 = new DatabaseConnection("DB-CONN-01");
DatabaseConnection c2 = new DatabaseConnection("DB-CONN-02");
DatabaseConnection.PrintGlobalPoolStatus(); // అవుట్పుట్: 2
11 🧹 డిస్ట్రక్టర్స్ / ఫైనలైజర్స్ (`~ClassName()`) & Garbage Collection
C++ లాంటి భాషల్లో ఆబ్జెక్ట్ పని అయిపోయాక మెమరీని మాన్యువల్గా delete చేయాలి.
కానీ C# లో Garbage Collector (GC) ఆటోమేటిక్గా అన్వాంటెడ్ ఆబ్జెక్ట్స్ను క్లీన్ చేస్తుంది.
ఒక ఆబ్జెక్ట్ మెమరీ నుండి పూర్తిగా నాశనం కాబోయే ముందు రన్ అవ్వడానికి C# లో డిస్ట్రక్టర్ లేదా ఫైనలైజర్ (Destructor / Finalizer) సింటాక్స్ ఉంటుంది:
⚠️ ప్రొఫెషనల్ హెచ్చరిక (Best Practice):
ఆధునిక .NET డెవలప్మెంట్లో డిస్ట్రక్టర్లను దాదాపుగా ఎప్పుడూ రాయకూడదు!
ఎందుకంటే ఫైనలైజర్ ఉన్న ఆబ్జెక్ట్లు మొదటి సైకిల్లో డిలీట్ అవ్వకుండా "Finalization Queue" లోకి వెళ్లి, Gen 1 లేదా Gen 2 మెమరీకి ప్రమోట్ అవుతాయి.
ఇది Garbage Collector పెర్ఫార్మెన్స్ను తీవ్రంగా దెబ్బతీస్తుంది. ఫైల్స్ లేదా నెట్వర్క్ సాకెట్లను క్లోజ్ చేయడానికి ఎల్లప్పుడూ IDisposable మరియు using స్టేట్మెంట్ వాడాలి (దీనిని మనం Chapter 17 లో నేర్చుకుంటాం).
12 🏢 రియల్-వరల్డ్ ప్రాజెక్ట్ — ఎంటర్ప్రైజ్ స్మార్ట్ బ్యాంకింగ్ సిస్టమ్ (Enterprise Smart Banking Engine)
ఈ అధ్యాయంలో మనం నేర్చుకున్న Classes, Objects, Primary Constructors (C# 12), Constructor Chaining, మరియు Static Members అన్నింటినీ కలిపి ఒక పూర్తి స్థాయి స్మార్ట్ బ్యాంకింగ్ కోర్ ఇంజిన్ (Core Banking Engine v1.0) నిర్మిద్దాం.
using System;
using System.Collections.Generic;
Console.OutputEncoding = System.Text.Encoding.UTF8;
Console.WriteLine("=================================================");
Console.WriteLine("🏦 మన బ్యాంక్ - ఎంటర్ప్రైజ్ కోర్ బ్యాంకింగ్ సిస్టమ్ v1.0");
Console.WriteLine("=================================================\n");
// 1. Transaction క్లాస్ - C# 12 Primary Constructor తో
public class BankTransaction(string txId, string type, decimal amount, string note)
{
public string TxId { get; } = txId;
public string Type { get; } = type;
public decimal Amount { get; } = amount;
public string Note { get; } = note;
public DateTime Timestamp { get; } = DateTime.UtcNow;
public void PrintDetails()
{
Console.WriteLine($" [{Timestamp:HH:mm:ss}] {Type,-8} | ₹{Amount,10:N2} | {Note} (ID: {TxId})");
}
}
// 2. BankAccount క్లాస్ - OOP ఫుల్ ఫీచర్స్ తో
public class BankAccount
{
// స్టాటిక్ మెంబర్స్ (బ్యాంక్ మొత్తం స్థాయి లెక్కలు)
public static int TotalAccountsCreated { get; private set; }
public static decimal BankTotalVaultReserves { get; private set; }
// ఇన్స్టాన్స్ ఫీల్డ్స్ & ప్రాపర్టీస్
public string AccountNumber { get; }
public string AccountHolderName { get; }
public decimal Balance { get; private set; }
private readonly List _transactions = new();
// స్టాటిక్ కన్స్ట్రక్టర్
static BankAccount()
{
TotalAccountsCreated = 0;
BankTotalVaultReserves = 0m;
Console.WriteLine("⚙️ సెంట్రల్ బ్యాంకింగ్ లెడ్జర్ ఇనిషియలైజ్ చేయబడింది.\n");
}
// మాస్టర్ కన్స్ట్రక్టర్
public BankAccount(string accNo, string holderName, decimal openingDeposit)
{
AccountNumber = accNo;
AccountHolderName = holderName;
Balance = openingDeposit;
TotalAccountsCreated++;
BankTotalVaultReserves += openingDeposit;
RecordTransaction("DEPOSIT", openingDeposit, "ప్రారంభ డిపాజిట్");
}
// సెకండరీ కన్స్ట్రక్టర్ (Chaining: కనీస ₹1,000 డిపాజిట్ తో)
public BankAccount(string accNo, string holderName) : this(accNo, holderName, 1000.00m)
{
}
public bool Deposit(decimal amount, string note = "నగదు జమ")
{
if (amount <= 0)
{
Console.WriteLine("❌ డిపాజిట్ మొత్తం సున్నా కంటే ఎక్కువగా ఉండాలి!");
return false;
}
Balance += amount;
BankTotalVaultReserves += amount;
RecordTransaction("DEPOSIT", amount, note);
Console.WriteLine($"✅ ₹{amount:N2} జమ విజయవంతం! ఖాతా: {AccountNumber} | బ్యాలెన్స్: ₹{Balance:N2}");
return true;
}
public bool Withdraw(decimal amount, string note = "నగదు విత్డ్రా")
{
if (amount <= 0)
{
Console.WriteLine("❌ మొత్తం సున్నా కంటే ఎక్కువగా ఉండాలి!");
return false;
}
if (amount > Balance)
{
Console.WriteLine($"❌ బ్యాలెన్స్ సరిపోదు! (ప్రస్తుత బ్యాలెన్స్: ₹{Balance:N2}, అభ్యర్థించినది: ₹{amount:N2})");
return false;
}
Balance -= amount;
BankTotalVaultReserves -= amount;
RecordTransaction("WITHDRAW", amount, note);
Console.WriteLine($"✅ ₹{amount:N2} విత్డ్రా పూర్తయింది! మిగిలిన బ్యాలెన్స్: ₹{Balance:N2}");
return true;
}
public bool Transfer(BankAccount targetAccount, decimal amount)
{
Console.WriteLine($"\n🔄 బదిలీ ప్రక్రియ: {this.AccountHolderName} -> {targetAccount.AccountHolderName} (₹{amount:N2})");
if (this.Withdraw(amount, $"బదిలీ -> {targetAccount.AccountNumber}"))
{
targetAccount.Deposit(amount, $"బదిలీ వచ్చింది <- {this.AccountNumber}");
Console.WriteLine("🎉 నిధుల బదిలీ విజయవంతంగా పూర్తయింది!");
return true;
}
Console.WriteLine("❌ నిధుల బదిలీ విఫలమైంది!");
return false;
}
private void RecordTransaction(string type, decimal amount, string note)
{
string txId = "TXN-" + Guid.NewGuid().ToString().Substring(0, 8).ToUpper();
_transactions.Add(new BankTransaction(txId, type, amount, note));
}
public void PrintAccountStatement()
{
Console.WriteLine($"\n-------------------------------------------------");
Console.WriteLine($"📄 మినీ స్టేట్మెంట్: {AccountHolderName} ({AccountNumber})");
Console.WriteLine($"💰 ప్రస్తుత నెట్ బ్యాలెన్స్: ₹{Balance:N2}");
Console.WriteLine($"లావాదేవీల చరిత్ర:");
foreach (var tx in _transactions)
{
tx.PrintDetails();
}
Console.WriteLine($"-------------------------------------------------");
}
}
// డ్రైవర్ కోడ్ (సిమ్యులేషన్)
var acc1 = new BankAccount("ACC-1001", "రామకృష్ణ శర్మ", 50000m);
var acc2 = new BankAccount("ACC-1002", "లావణ్య దేవి"); // డీఫాల్ట్ ₹1,000 తో
acc1.Deposit(15000m, "క్లయింట్ ప్రాజెక్ట్ పేమెంట్");
acc1.Withdraw(8000m, "ఆఫీస్ రెంట్ పేమెంట్");
acc1.Transfer(acc2, 20000m);
acc1.PrintAccountStatement();
acc2.PrintAccountStatement();
Console.WriteLine($"\n📊 బ్యాంక్ మొత్తం గణాంకాలు:");
Console.WriteLine($"మొత్తం ఖాతాలు : {BankAccount.TotalAccountsCreated}");
Console.WriteLine($"బ్యాంక్ మొత్తం నిధులు: ₹{BankAccount.BankTotalVaultReserves:N2}");
Console.WriteLine("=================================================");
💻 ఈ ప్రాజెక్ట్ను లైవ్ కంపైలర్లో రన్ చేసి పరీక్షించండి!
ఆబ్జెక్ట్స్ క్రియేషన్, Constructor Chaining, డిపాజిట్లు మరియు ఇంట్రా-బ్యాంక్ ట్రాన్స్ఫర్లను నిజమైన .NET 8 ఎన్విరాన్మెంట్లో చూడండి.
13 🎓 ఇంటర్వ్యూ ప్రశ్నలు, బెస్ట్ ప్రాక్టీసెస్ & 5 ఇంటరాక్టివ్ క్విజ్ ప్రశ్నలు
టాప్ ఇంటర్వ్యూ ప్రశ్నలు & సమాధానాలు:
Q1: C# లో Class కి Struct కి మధ్య ముఖ్యమైన తేడా ఏమిటి?
జవాబు: Class అనేది Reference Type, దీని ఆబ్జెక్ట్ Managed Heap లో స్టోర్ అవుతుంది. Struct అనేది Value Type, ఇది Stack లో స్టోర్ అవుతుంది. క్లాసెస్ ఇన్హెరిటెన్స్ను సపోర్ట్ చేస్తాయి, కానీ స్ట్రక్ట్స్ ఇన్హెరిట్ చేసుకోలేవు. చిన్నపాటి లైట్వెయిట్ డేటాకు Structs, భారీ బిజినెస్ లాజిక్కు Classes వాడతారు.
Q2: C# 12 Primary Constructor పారామీటర్లు ఫీల్డ్స్గా మారుతాయా?
జవాబు: Primary Constructor పారామీటర్లు ఆటోమేటిక్గా పబ్లిక్ ప్రాపర్టీలుగా మారవు (Records లాగా కాదు). అవి క్లాస్ పరిధిలో ఉండే ప్రైవేట్ వేరియబుల్స్ లా పనిచేస్తాయి. మీరు వాటిని మెథడ్స్లో వాడితే కంపైలర్ లోపల ఒక బ్యాకింగ్ ఫీల్డ్ను క్లాస్లో జనరేట్ చేస్తుంది.
🧠 మీ నాలెడ్జ్ చెక్ చేసుకోండి — 5 ఇంటరాక్టివ్ క్విజ్ ప్రశ్నలు
❓ ప్రశ్న 1: Car c1 = new Car(); Car c2 = c1; రాసినప్పుడు ఎన్ని ఆబ్జెక్ట్లు Heap లో క్రియేట్ అవుతాయి?
new ఆపరేటర్ ఒక్కసారే పిలవబడింది కాబట్టి హీప్లో ఒకే ఆబ్జెక్ట్ ఉంటుంది. c2 అనేది అదే ఆబ్జెక్ట్ మెమరీ అడ్రస్ను సూచించే రెండో రిఫరెన్స్ వేరియబుల్ మాత్రమే.
❓ ప్రశ్న 2: ఒక క్లాస్లో పారామీటరైజ్డ్ కన్స్ట్రక్టర్ రాస్తే, డీఫాల్ట్ కన్స్ట్రక్టర్ new Class() ఏమవుతుంది?
new Class() కాల్ చేయడం కంపైలేషన్ ఎర్రర్ అవుతుంది.
❓ ప్రశ్న 3: ఒక కన్స్ట్రక్టర్ నుండి ఇంకో కన్స్ట్రక్టర్ను కాల్ చేయడానికి ఏ సింటాక్స్ వాడాలి?
: this(...) (Constructor Chaining). ఇది కోడ్ డూప్లికేషన్ను నివారించి క్లీన్ కోడ్ రాయడానికి సహాయపడుతుంది.
❓ ప్రశ్న 4: Static కన్స్ట్రక్టర్ ఎప్పుడు రన్ అవుతుంది? దానికి పారామీటర్లు ఇవ్వవచ్చా?
❓ ప్రశ్న 5: C# 12 లో ప్రైమరీ కన్స్ట్రక్టర్ ఉన్న క్లాస్లో సెకండరీ కన్స్ట్రక్టర్ రాస్తే తప్పనిసరి నిబంధన ఏమిటి?
: this(...) ద్వారా ప్రైమరీ కన్స్ట్రక్టర్ను నేరుగా గానీ లేదా ఇండైరెక్ట్గా గానీ కాల్ చేయాలి. లేకపోతే కంపైల్-టైమ్ ఎర్రర్ వస్తుంది.