C# में Generics क्या हैं और इनकी आवश्यकता क्यों है
कल्पना करें कि आप एक ऐसी विधि लिख रहे हैं जो विभिन्न डेटा प्रकारों के साथ काम करनी चाहिए: पूर्णांक, स्ट्रिंग, ऑब्जेक्ट। Generics (सामान्यीकरण) के बिना, आपको या तो प्रत्येक प्रकार के लिए कोड दोहराना होगा, या object का उपयोग करके प्रकारों को परिवर्तित करना होगा। दोनों ही दृष्टिकोण समस्याएँ पैदा करते हैं: पहला अतिरेकता की ओर ले जाता है, दूसरा रनटाइम त्रुटियों की ओर।
Generics (सामान्यीकृत प्रकार) C# में एक तंत्र है जो आपको क्लासेज, विधियाँ, इंटरफेस और डेलिगेट बनाने की अनुमति देता है जो किसी भी डेटा प्रकार के साथ काम करते हैं, साथ ही सख्त टाइपिंग बनाए रखते हैं। किसी विशिष्ट प्रकार के लिए कोड लिखने के बजाय, आप एक प्रकार पैरामीटर (आमतौर पर अक्षर T द्वारा दर्शाया जाता है) का उपयोग करते हैं, जो उपयोग के समय वास्तविक प्रकार से बदल दिया जाता है।
Generics के मुख्य लाभ:
- प्रकार सुरक्षा — कंपाइलर संकलन के समय प्रकारों के सही उपयोग की जाँच करता है
- प्रदर्शन — मूल्य प्रकारों के लिए बॉक्सिंग/अनबॉक्सिंग (boxing/unboxing) का अभाव
- कोड पुन: उपयोग — एक क्लास/विधि कई प्रकारों के साथ काम करती है
- पठनीयता — कोड साफ और समझने में आसान हो जाता है
Generics के बिना समस्या: object पर उदाहरण
Generics के आगमन से पहले, डेवलपर्स अक्सर सार्वभौमिक विधियाँ बनाने के लिए object प्रकार का उपयोग करते थे। एक उदाहरण देखें:
public class Stack{ private object[] _items = new object[10]; private int _count = 0; public void Push(object item) { _items[_count++] = item; } public object Pop() { return _items[--_count]; } // उपयोग Stack stack = new Stack(); stack.Push(123); // int को object में बॉक्सिंग stack.Push("hello"); // कोई भी प्रकार जोड़ा जा सकता है int number = (int)stack.Pop(); // अनबॉक्सिंग और परिवर्तन}समस्याएँ स्पष्ट हैं:
- निकालते समय स्पष्ट प्रकार परिवर्तन की आवश्यकता होती है
- गलती से दूसरे प्रकार का मान जोड़ा जा सकता है
- मूल्य प्रकारों (int, double) के लिए बॉक्सिंग/अनबॉक्सिंग होती है, जो प्रदर्शन को कम करती है
C# में Generics के साथ काम करने की मूल बातें
सामान्यीकृत क्लास बनाना
उसी स्टैक उदाहरण को Generics के साथ देखें:
public class Stack<T>{ private T[] _items = new T[10]; private int _count = 0; public void Push(T item) { _items[_count++] = item; } public T Pop() { return _items[--_count]; }}
// उपयोगStack<int> intStack = new Stack<int>();intStack.Push(123);intStack.Push(456);int number = intStack.Pop(); // अनबॉक्सिंग की आवश्यकता नहीं, प्रकार ज्ञात है
Stack<string> stringStack = new Stack<string>();stringStack.Push("Hello");// stringStack.Push(123); // संकलन त्रुटि! प्रकार सुरक्षापैरामीटर <T> को कोणीय कोष्ठकों में निर्दिष्ट किया जाता है। इंस्टेंस बनाते समय, आप एक विशिष्ट प्रकार निर्दिष्ट करते हैं, और कंपाइलर उस प्रकार के लिए क्लास का एक विशेष संस्करण उत्पन्न करता है।
सामान्यीकृत विधियाँ
Generics न केवल क्लासेज के साथ, बल्कि अलग-अलग विधियों के साथ भी काम करते हैं:
public class Utils{ public static T Max<T>(T a, T b) where T : IComparable<T> { return a.CompareTo(b) > 0 ? a : b; }}
// उपयोगint maxInt = Utils.Max(5, 10); // 10string maxStr = Utils.Max("apple", "banana"); // "banana"Generics में प्रतिबंध (Constraints)
कभी-कभी उन प्रकारों को प्रतिबंधित करने की आवश्यकता होती है जिनका उपयोग सामान्यीकरण के साथ किया जा सकता है। इसके लिए where कीवर्ड के साथ प्रतिबंध (constraints) का उपयोग किया जाता है:
// प्रतिबंध: T एक संदर्भ प्रकार होना चाहिएpublic class Repository<T> where T : class{