C# Generic Types

Default Value for C# Generic Types Parameter

जब हम Generic Code Create करते हैं, तब कई बार हमें समान समय पर Value Type Reference Type दोनों के साथ Deal करना होता है। इस तरह की Situation सामान्‍यत: तब पैदा होती है, जब हम Type Parameter Variable को एक Default Value Set करना चाहते हैं। क्योंकि Reference Types को तो हम Default Value के रूप में null Assign कर सकते हैं। जबकि Value Types के लिए हमें 0 या false जैसी Value को Default Value के रूप में Assign करना होता है। साथ ही struct Type भी एक Object होता है, जिसके सभी Fields को उनके Type के अनुसार Default Value से Set करना होता है।

अत: परेशानी तब पैदा होती है, जब हम Parameter Types को Use करते हैं, क्योंकि हम जानते हैं कि Generic का Type जरूरत के अनुसार Specify किया जाता है, इसलिए Generic Type Define करते समय हमें पता ही नहीं होता कि Generic के रूप में किस Type को Specify किया जाएगा। परिणामस्वरूप हम कभी भी Generic Type के Members को Normal तरीके से Default Value Set नहीं कर सकते। जैसे:

    class GenClass<T>
    {
        T genObject; 
	. . .
    }

यदि हम इस Generic Class में genObject को Default Value के रूप में निम्नानुसार null Initialize करें:

T genObject = null;

तो सभी Reference Types के लिए ये Initialization Normal तरीके से काम करेगा, लेकिन Value Types के लिए ये Initialization Error Generate करेगा। इसी तरह से यदि हम इस Generic Class में genObject को Default Value के रूप में निम्नानुसार 0 Initialize करें:

T genObject = 0;

तो सभी Value Types के लिए ये Initialization Normal तरीके से काम करेगा, लेकिन Reference Types के लिए ये Initialization Error Generate करेगा।

इस समस्या के समाधान के रूप में C# हमें default Keyword Provide करता है, जिसे हम निम्नानुसार तरीके से Use करते हैं:

default(type)

इसलिए अब यदि हम इस तरीके को Use करते हुए निम्नानुसार Initialization करें:

T genObject = default(T);

तो ये Initialization सभी Parameter Types के लिए समान प्रकार से काम करेगा, भले ही Parameter Type, Value Type हो या Reference Type हो। इस Concept के आधार पर हम निम्नानुसार एक Demo Program Create कर सकते हैं:

File Name: defaultKeyword.cs
using System;

namespace CSharpGenerics
{
    class BaseClass { }
    class GenClass<T>
    {
        public T genObject;

        public GenClass()
        {
            genObject = default(T);
        }
    }

    class defaultKeyword
    {
        static void Main()
        {
            GenClass<int> valType = new GenClass<int>();
            if (valType.genObject == 0)
                Console.WriteLine("Default Value for Value Type: 0");

            GenClass<BaseClass> refType = new GenClass<BaseClass>();
            if (refType.genObject == null)
                Console.WriteLine("Default Value for Reference Type: null");
        }
    }
}

// Output:
   Default Value for Value Type: 0
   Default Value for Reference Type: null

Generic Structures

चूंकि Structure व Class दोनों को ही C# में नया User Defined Type Define करने के लिए समान प्रकार से Use किया जा सकता है, इसलिए जिन-जिन Concepts को हमने पिछले Programs में Generic Class Implement करने के लिए समझा है, उन सभी को हम Structure Type के लिए भी समान प्रकार से Use कर सकते हैं। यानी हम Structure Type के Generics के लिए भी Constraints को Use कर सकते हैं और Default Values Set करने के लिए default Keyword को Use कर सकते हैं।

यानी Generic Structure व Generic Class के Implementation में कोई अन्तर नहीं है। अन्तर केवल इनके Memory में Store होने के तरीके में ही होता है। क्योंकि Class Type एक Reference Type होता है, इसलिए हमेंशा Memory के Heap Area में Store होता है। जबकि C# में Structure Type हमेंशा Value Type होता है, इसलिए Memory के Stack Area में Storage Space Reserve करता है।

File Name: GenericStructure.cs
using System;

namespace CSharpGenerics
{
    struct Coordinate<T>
    {
        T x;
        T y;

        public Coordinate(T a, T b)
        {
            x = a;
            y = b;
        }

        public T X
        {
            get { return x; }
            set { x = value; }
        }

        public T Y
        {
            get { return y; }
            set { y = value; }
        }
    }

    class defaultKeyword
    {
        static void Main()
        {
            Coordinate<int> screen = new Coordinate<int>(10, 20);
            Coordinate<double> monitor = new Coordinate<double>(88.0, 99.0);
            Console.WriteLine("Pixel Position on Screen: " + screen.X + ", " + screen.Y);
            Console.WriteLine("Pixel Position on Monitor: " + monitor.X + ", " + monitor.Y);
        }
    }
}

// Output:
   Pixel Position on Screen: 10, 20
   Pixel Position on Monitor: 88, 99

Generic Methods

जिस तरह से हम किसी Method में Parameter व Return Value के रूप में किसी Primitive Type अथवा Reference Type को Specify करते हैं, ठीक उसी तरह से हम Generic Type को भी Specify कर सकते हैं और इस Chapter की शु:आत में हमने Swap() नाम का Generic Method का एक उदाहरण भी देखा था।

साथ ही इस Chapter के विभिन्न Programs की Class के Constructor Display() Method में भी हमने Generic Types Use किया है। हम ऐसा इसलिए कर सकते हैं क्योंकि Program के Compile Time में Generic Type के Generic को Parameter Type द्वारा Replace कर दिया जाता है।

परिणामस्वरूप हर Generic वास्तव में एक Appropriate Type से Replace हो जाता है और हमारे Method के Generic Type के सभी Local Variables, Parameters Return Values का Type, Parameter Type के रूप में Specified Type के अनुसार बदल जाता है। जिसकी वजह से हमारे Program का हर Generic Method एक Normal Method की तरह ही काम करता है। जिसके बारे में हम पहले ही Discuss कर चुके हैं।

जिस तरह से हम Generic ClassGeneric Structure Type के साथ विभिन्न प्रकार के Constraints का प्रयोग कर सकते हैं, उसी तरह से हम Generic Method में Use किए जाने वाले Generics के साथ ही where Clause का प्रयोग करके Constraints Specify कर सकते हैं। जैसे:

	public static void GenericMethod<T>(T arg1, T[] arg2, . . .) where T : class 
	{
		// . . . 
	}

Generic Method की विशेषता ये होती है कि हम Generic या Non-Generic किसी भी प्रकार के Type (Structure, Class, etc…) में Generic Method को Define करके Use कर सकते हैं। जैसाकि उपरोक्त Syntax द्वारा हम समझ सकते हैं कि Generic Method की हमेंशा दो Parameter List होती है। Method Argument List को Parenthesis के बीच Enclose किया जाता है, जबकि Type Parameter List को Angle Brackets के बीच Enclose करते हैं। किसी Generic Method को Implement करते समय हमें निम्नानुसार Syntax का प्रयोग करना होता है:

C# Generic Types - Hindi. Parameter List

जबकि Generic Method को Invoke करने के लिए हमें निम्नानुसार Syntax को Follow करना होता है:

C# Generic Types - Hindi. Type Arguments

उदाहरण के लिए यदि हम हमारे MyMethod नाम के Generic Method को DoStuff() नाम से Represent करें, तो दो अलग Generic Methods की Implementation को निम्न चित्रानुसार दर्शाया जा सकता है:

C# Generic Types - Hindi. Type Arguments

Extension Method with Generic Class

Extension Method के बारे में हम पहले भी बात कर चुके हैं। Extension Methods, Generic Classes के साथ भी समान प्रकार से काम करते हैं, जिस तरह से किसी Non-Generic Class के साथ करते हैं।

यानी Extension Method का प्रयोग करके हम किसी एक Static Class में Defined किसी Static Method को, किसी Different Generic Classes में Defined Methods को Invoke करने के लिए Use कर सकते हैं। हालांकि ये Static Method किसी दूसरी Class में Define किए गए होते हैं, लेकिन हम इन्हीं किसी भी Related Class के Member की तरह उसके Object के साथ Use कर सकते हैं।

Non-Generic Class के Extension Method की तरह ही Generic Class के Extension Method को भी निम्न Rules को Follow करना जरूरी होता है:

  • Extension Method का static Method Declared होना जरूरी होता है।
  • Extension Method को किसी static Class का Member होना जरूरी होता है।
  • Extension Method के First Parameter के रूप में this Keyword को Specify किया जाना जरूरी होता है और this के बाद उस Class का नाम Specify करना होता है, जिसके Objects के लिए Extension Method को Member की तरह Call किया जाना होता है।

इस तरह से यदि हम उपरोक्त तीनों नियमों को ध्‍यान में रखते हुए Generic Class के लिए एक Extension Method Define करें, तो हमारा बनने वाला Program कुछ निम्नानुसार हो सकता है:

File Name: GenericExtensionMethod.cs
using System;

namespace CSharpGenerics
{
    static class ExtensionMethod
    {
        public static void Print<T>(this GenericMethodClass<T> h)
        {
            T[] vals = h.GetValues();
            Console.WriteLine("{0},\t{1},\t{2}", vals[0], vals[1], vals[2]);
        }
    }

    class GenericMethodClass<T>
    {
        T[] Vals = new T[3];
        public GenericMethodClass(T v0, T v1, T v2)
        {
            Vals[0] = v0;
            Vals[1] = v1;
            Vals[2] = v2;
        }

        public T[] GetValues()
        {
            return Vals;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var intGeneric = new GenericMethodClass<int>(30, 15, 57);
            var stringGeneric = new GenericMethodClass<string>("Kuldeep", "Mukesh", "Rahul");
            intGeneric.Print();
            stringGeneric.Print();
        }
    }
}

// Output:
   30,	        15,	    57
   Kuldeep,	Mukesh,	    Rahul

इस Program में हमने Generic Class के लिए एक Extension Method Define किया है। Extension Method यहां भी Exactly वैसा ही काम कर रहा है, जिस तरह से Normal Extension Method काम करता है। अन्तर केवल इतना है कि यहां Define किया गया Extension Method एक Generic Class के लिए Define किया गया है।

Generic Delegates

Generic Delegates भी Non-Generic Delegate की तरह ही होते हैं। अन्तर केवल इतना होता है कि Generic Delegates का Type तब तय होता है जब Delegate Object के साथ Parameter Type को Specify किया जाता है।

चूंकि Delegates भी एक प्रकार का User Defined Type ही होते हैं, जिन्हें Method की तरह Declare किया जाता है, इसलिए हम Delegate Types के लिए भी Generic Create कर सकते हैं। जब हम Generic Delegate Create करना चाहते हैं, तब हमें निम्नानुसार Statement Use करना होता है:

delegate ReturnType DelegateName<ParameterList>(ArgumentList);

इस Syntax के अनुसार हमें Delegate के नाम के तुरन्त बाद Parameter List को Specify करना होता है। Delegate Type का फायदा ये होता है कि हम Type-Safe तरीके से एक ऐसा Generalized Form Create कर सकते हैं जो कि सभी Matching Methods के Compatible हो। जैसे:

C# Generic Types - Hindi. Type Arguments

Generic Delegate के Concepts को Use करते हुए हम निम्नानुसार एक Program Create कर सकते हैं, जो कि Generic Delegate की Working को समझने में हमारी मदद करेगा:

File Name: GenericDelegate.cs
using System;

namespace CSharpGenerics
{
    delegate T GenericDelegate<T>(T v);

    class UsingGenericDelegate
    {
        static int Sum(int v)
        {
            int result = 0;
            for (int i = v; i > 0; i--)
                result += i;
            return result;
        }

        static string ReverseString(string str)
        {
            string result = "";
            foreach (char ch in str)
                result = ch + result;
            return result;
        }

        static void Main()
        {
            // Construct an int delegate.
            GenericDelegate<int> intDelegate = Sum;
            int value = 5;
            Console.WriteLine("Total of 1 to {0} : {1}", value, intDelegate(value));

            // Construct a string delegate.
            GenericDelegate<string> strDelegate = ReverseString;
            Console.WriteLine(strDelegate("This is Reversed String."));
        }
    }
}

// Output:
   Total of 1 to 5 : 15
   .gnirtS desreveR si sihT

इस Program में हमने निम्नानुसार तरीके से एक Delegate Create किया है, जो कि एक Generic Type का मान Return करता है:

delegate T GenericDelegate<T>(T v);

यानी जब Main() Method का निम्न Statement Execute होता है:

GenericDelegate<int> intDelegate = Sum;

तो Parameter Type Generic के रूप में int Specify करने की वजह से हमारा Delegate निम्नानुसार Modify हो जाता है:

delegate int GenericDelegate<int>(int v);

परिणामस्वरूप intDelegate नाम के Delegate Type के Object की Invocation List में Sum() नाम के Method का Reference Store हो जाता है। फलस्वरूप जब निम्न Statement Execute होता है:

int value = 5;
Console.WriteLine(“Total of 1 to {0} : {1}”, value, intDelegate(value));

तो intDelegate में Stored Sum() नाम का Callback Method Execute हो जाता है और Parameter के रूप में Pass किए गए value के मान तक का Summation Return करके Display कर देता है। इसी तरह से जब Main() Method का निम्न Statement Execute होता है:

GenericDelegate<string> strDelegate = ReverseString;

तो इस बार Parameter Type Generic के रूप में string Specify करने की वजह से हमारा Delegate निम्नानुसार Modify हो जाता है:

delegate string GenericDelegate<string>(string v);

परिणामस्वरूप strDelegate नाम के Delegate Type के Object की Invocation List में ReverseString() नाम के Method का Reference Store हो जाता है। फलस्वरूप जब निम्न Statement Execute होता है:

Console.WriteLine(strDelegate(“This is Reversed String.”));

तो strDelegate में Stored ReverseString() नाम का Callback Method Execute हो जाता है और Parameter के रूप में Pass किए गए string को Reverse करके Display कर देता है। इस तरह से हमें हमारे Program का उपरोक्तानुसार Output प्राप्त होता है।

हम देख सकते हैं कि Generics का प्रयोग Delegate के साथ करके हम किस तरह से अलग-अलग Types के लिए एक ही Generic को अलग-अलग तरह की Requirements को पूरा करने के लिए Implement कर सकते हैं।

यानी जब हम Generic के रूप में int Parameter Type Specify करते हैं, तो हमारे Delegate का Signature व Return Type Sum() नाम के Method से Match हो जाता है, जिसकी वजह से Delegate के माध्‍यम से Sum() Method को Invoke कर दिया जाता है और जब हम Generic के रूप में string Parameter Type Specify करते हैं, तो हमारे Delegate का Signature व Return Type ReverseString() नाम के Method से Match हो जाता है, जिसकी वजह से Delegate के माध्‍यम से ReverseString() Method को Invoke कर दिया जाता है

Generic Interfaces

Interface भी एक प्रकार का User Defined Type ही होता है, इसलिए किसी भी अन्‍य User Defined Type की तरह ही हम Generic Interface भी Create कर सकते हैं। जैसे:

	public interface IGeneric<T>
	{
		void Display();
	}

जब हम Generic Interface Type Create करते हैं, तो इस Generic Interface को किसी Normal Class में Implement नहीं किया जा सकता। बल्कि Generic Interface को हमेंशा Generic Class में ही Implement कर सकते हैं।

इसलिए यदि हम इस IGeneric Interface को Implement करना चाहें, तो हमें निम्नानुसार तरीके से एक Generic Class में ही इस Interface को Implement करना होगा:

	public class GenericClass<T> : IGeneric<T>
	{
		T genObject;

		public GenericClass(T argObject) 
		{
		genObject = argObject;
		}

		void Display()
		{
			Console.WriteLine("Value: " + genObject);
		}
	}

यदि हम इस Generic Class Generic Interface के आधार पर एक Demo Program Create करें, तो हमारा Program कुछ निम्नानुसार हो सकता है:

File Name: GenericInterface.cs
using System;

namespace CSharpGenerics
{
    public interface IGeneric<T>
    {
        void Display();
    }

    public class GenericClass<T> : IGeneric<T>
    {
        T genObject;

        public GenericClass(T argObject)
        {
            genObject = argObject;
        }

        public void Display()
        {
            Console.WriteLine("Value: " + genObject);
        }
    }

    class UsingGenericInterface
    {
        static void Main()
        {
            GenericClass<string> msg = new GenericClass<string>("Kuldeep Mishra");
            msg.Display();
        }
    }
}

Output:
	Value: Kuldeep Mishra

चूंकि Generic Class में यदि हम Parameter Type को Specify कर दें, तो वह Class भी एक Constructed Type की तरह Define हो जाता है। इसलिए हम एक Generic Class पर भी उन सभी प्रकार के Operations Perform कर सकते हैं, जिन्हें किसी Non-Generic Class पर Perform किया जा सकता है।

यानी हम एक Generic Base Class को किसी दूसरी Generic Class के रूप में Derive कर सकते हैं। किसी Non-Generic Class से Derived Class को एक Generic Class की तरह Define कर सकते हैं। Generic Class में Interfaces को Implement कर सकते हैं। Generic Classes के साथ Polymorphism की सुविधा प्राप्त करने के लिए Code कर सकते हैं। इसी तरह से Generic Methods की Overloading कर सकते हैं। यानी हम जो भी काम किसी Class के साथ कर सकते हैं, उन सभी कामों को Generic Class के साथ भी कर सकते हैं।

बस Generic Classes के साथ ये प्रक्रियाऐं करते समय हमें केवल इतना ही ध्‍यान रखना होता है कि हम किसी Actual Type के स्थान पर किसी Generic Type के लिए इन सभी Operations को Implement करते हैं और Generic के स्थान पर जिस तरह का Type Specify किया जाना होता है, उसे Compiler द्वारा Compile Time में तय किया जाता है। इसलिए हमें Reference Type या Value Type के Generic को ध्‍यान में रखना होता है कि किस तरह के Data के लिए Generic किस तरह का Action Perform करेगा।

हालांकि हम Generics को Methods के साथ Use करते हुए Generic Methods Define कर सकते हैं, लेकिन फिर भी हम Properties, OperatorsIndexers को Generic नहीं बना सकते। साथ ही हम Generic Method के साथ extern Keyword का भी प्रयोग नहीं कर सकते। Generic के साथ Pointer Types को Type Argument की तरह Use नहीं किया जा सकता।

जबकि यदि किसी Generic Class में कोई static Field हो, तो हर Constructed Type में उस Static Field की अपना स्वयं का अलग Copy होता है। यानी समान Constructed Type के सभी Instances उस Static Field की समान Copy को Share करते हैं, लेकिन अगल-अलग Constructed Types के सभी Static Fields अलग-अलग होते हैं। सरल शब्दों में कहें तो एक ही Generic के अलग-अलग Constructed Type के लिए अलग-अलग Static Field होते हैं। यानी सभी Constructed Types समान Static Field को Share नहीं करते।

यदि हमें एक ही Generic Class में एक से ज्यादा Interfaces को Implement करना हो, तो हमें सभी Interfaces के नाम को एक Comma Separated List के रूप में ही Specify करना होता है। जैसे:

public class GenericClass<T> : IGeneric<T>, IComparer<T>, IClonable<T>

लेकिन ठीक इसी तरह से हम Class को Comma Separated List के रूप में Specify नहीं कर सकते, क्योंकि C# Multiple Inheritance को Support नहीं करता लेकिन Multiple Interface को Support करता है। इसलिए उपरोक्त Statement Normal तरीके से Run होगा, लेकिन निम्न Statement Error Return करेगा:

public class GenericClass<T> : BaseClass<T>, OtherBaseClass<T>

जब हम Generic Interface Create करते हैं, तब हमें हमेंशा ऐसा Generic Interface Define करना होता है, जो कि किसी भी दूसरे Interface के किसी Generic Declaration से Match न हो। अन्‍यथा Duplicate Interface Method Implementation की वजह से Compile Time Error Return होता है।

C# in Hindiये Article इस वेबसाईट पर Selling हेतु उपलब्‍ध EBook C#.NET in Hindi से लिया गया है। इसलिए यदि ये Article आपके लिए उपयोगी रहा, तो निश्चित रूप से ये पुस्तक भी आपके लिए काफी उपयोगी साबित होगी। 

C#.NET in Hindi | Page:908 | Format: PDF

BUY NOW GET DEMO REVIEWS