Asynchronous Programming – C# GUI

Asynchronous Programming – C# GUI: हालांकि हमने इस Chapter में Multi-Threading व Multi-Tasking से सम्बंधित बहुत सारे Console Mode Programs Create किए हैं और .NET Framework द्वारा इन विषयों से सम्बंधित विभिन्न Concepts के बारे में जाना है, लेकिन Asynchronous Programming मूल रूप से GUI Programming के लिए ही उपयोगी है।

क्योंकि एक GUI Program को इस तरह से Design किया जाता है कि Program से सम्बंधित विभिन्न GUI Controls एक साथ एक ही Form पर दिखाई देते हैं और यदि कोई Control कोई Time Consuming Heavy Task Perform करने लग जाए, तो Form के सभी अन्‍य Controls पूरी तरह से तब तक के लिए Block हो जाएेंगे, जब तक कि उस Particular Control का Task पूरा न हो जाए।

GUI Windows Program एक Special तरीके से Messages (Methods) का प्रयोग Develop किए जाते हैं, जहां Program का हर Event Handler Method (Message) एक Message Queue में Placed रहता है और इस Queue को एक Message Pump द्वारा Manage किया जाता है।

जब GUI Application Run होता है और User उस GUI Program के किसी Control के साथ किसी प्रकार का Interaction करता है, तो उस Interaction को Perform करने के लिए Message Pump, Message Queue से एक Message को Retrieve करता है और उस Message से सम्बंधित Event Handler Code को Call करता है।

जब Event Handler Code पूरी तरह से Execute हो जाता है, तब Message Pump फिर से Message Queue से अगला Message Retrieve करता है और उस अगले Message के Handler Code को Execute करता है। GUI Program में विभिन्न Events के लिए हर User Interaction के Response में यही Process Follow होता है।

GUI Based Windows Program के इस तरह के Architecture के कारण ये जरूरी होता है कि Handler Code छोटा हो, ताकि वह जल्दी से Execute हो जाए और किसी अन्‍य Control की Working को Block न करे।

लेकिन यदि Handler Code काफी बडा हो या कोई Time Consuming Slow Task Perform करता हो, तो जब तक वह Code पूरी तरह से Execute नहीं हो जाताए तब तक पूरा GUI Program Unresponsive हो जाता है। इस स्थिति में जब कोई ऐसा Time Consuming Task पूरा किया जाना होता है, तो हम किसी Message (Event Method) के लिए Normal तरीके का Synchronous Handler Code नहीं लिख सकते।

बल्कि हमें इस Slow Performing Task को Asynchronous तरीके से ही पूरा करना जरूरी होता है, क्योंकि ये Code GUI Program को पूरी तरह से Block कर देता है। Blocking की इस प्रक्रिया को हम निम्नानुसार एक GUI Example Program द्वारा आसानी से समझ सकते हैं।

जब हम GUI Program की बात कर रहे हैं तो फिर हमें हमारा Example भी एक GUI Program के रूप में ही Create करना होगा। हालांकि यदि हम चाहें तो किसी GUI Program को भी Notepad ++ के Text Editor में Manual Coding द्वारा Create, Compile व Run कर सकते हैं, लेकिन ये एक काफी Detailed और Time Consuming काम हो जाएगा।

क्योंकि Visual Programs के User Interface को जितना आसानी से और कम समय में Visually Create किया जा सकता है, उतनी Accuracy के साथ Manual Coding द्वारा नहीं Create किया जा सकता।

चूंकि Visual User Interface Design करने के लिए हमारे पास Visual Studio व SharpDevelop जैसे Software पहले से ही Exist हैं, तो Visual User Interface Design करने में समय खराब करने का कोई मतलब नहीं है। साथ ही यदि Processional Development के आधार पर भी देखें, तो Companies को Fast Development चाहिए और Fast Development कभी भी Manual Coding द्वारा नहीं हो सकता।

हालांकि हमने इस पुस्तक के लगभग सभी Programs Console Mode Applications के रूप में ही Create किए हैं, क्योंकि किसी भी Programming Language के Logic को समझने के लिए Console Mode Applications द्वारा छोटे-छोटे Examples के माध्‍यम से Language Constructs व Concepts को समझना ठीक रहता है।

लेकिन जब Professional Development की बात आती है, तब हम Manual Coding पर Depend नहीं हो सकते। क्योंकि Processional Development में Software की कीमत उस Software को Develop करने में लगने वाले समय पर आधारित होती है और Manual Coding में हमेंशा ज्यादा समय लगता है। इसलिए Software Development की कीमत को कम से कम रखने के लिए ही विभिन्न प्रकार के IDEs बना, गए हैं, ताकि Software Development Fast हो सके।

जब हम Programmer के रूप में एक Professional Software Development की बात करते हैं, तब हमें एक बात और ध्‍यान में रखनी होती है कि कोई भी Software किसी एक Single Programming Language या एक Single Technology को Use करते हुए नहीं बनाया जा सकता। बल्कि एक Single Form Based Application Software बनाने में भी हमें बहुत सारी Technologies को Use करना होता है।

उदाहरण के लिए यदि किसी भी Software का एक Frontend होता है, जिससे User Interact करता है। इस User Interface के लिए हमें एक अलग Technology को Use करना होता है। जबकि केवल इस User Interface को Design करे के लिए भी हमारे पास कई Technologies हैं।

यदि हम केवल Microsoft द्वारा Provided Technologies को देखें, तो Frontend Develop करने के लिए Microsoft, Windows Forms व WPF (Windows Presentation Foundation) नाम के दो तरीके Available करवाता है।

इतना ही नहीं WPF पूरी तरह से एक XML Based Language XAML नाम की Markup Language पर आधारित है। हालांकि हम C# का प्रयोग करके बिना XAML का प्रयोग किए हुए भी Frontend User Interface Develop कर सकते हैं।

जबकि वर्तमान समय में HTML/XML Based कई Languages हैं, जिनका प्रयोग Application का Frontend Create करने के लिए किया जाता है, ताकि Applications को Distributed Form में Internet के माध्‍यम से Globally Access किया जा सके।

इसी तरह से जब हम कोई Application Create करते हैं, तो Data को Store करने के लिए या तो हम File System को Use करते हैं या फिर हमें किसी न किसी Database System (MSSQL Server, MySql, Oracle, etc…) को Use करना होता है।

Frontend User Interface व Backend Database के अलावा हमें हर Application Software में Output को Properly Printable Format में Generate करने के लिए भी Crystal Reports जैसे किसी Software को Use करना होता है या फिर HTML जैसी किसी Markup Language को CSS जैसी Styling Language के साथ Use करते हुए Web-Browser Based Output यानी Reports Create करने होते हैं, ताकि Application के Output को Printable Format में प्राप्त किया जा सके।

इन सभी के अलावा Development, Deployment, Distribution आदि से सम्बंधित विभिन्न प्रकार की अन्‍य जरूरतों को पूरा करने के लिए भी हमें कई प्रकार की Languages व Tools को Use करना जरूरी होता है।

इसलिए यदि हम बिना कोई Tool Use किए हुए इन सभी जरूरतों को पूरा करने की कोशिश करें, तो एक Simple से Single Form के Application Software को Develop करने में भी महीनों लग जाएेंगे।

इसीलिए Software Development को Fast व Best Manageable तथा Extendable बना, रखने के लिए ही विभिन्न प्रकार के IDEs व Tools Create किए गए हैं और Professional Development के दौरान हमें इन्हें Use करना ही होता है।

अत: हम हमारे Example GUI Program को न तो Manually Code करेंगे न ही Manually Compile करेंगे बल्कि हम Microsoft द्वारा Provided Visual Studio IDE को Use करेंगे। क्योंकि यदि हम एक Simple सा GUI Form भी Manually Create करना चाहें, तो एक Simple से GUI Form को Create करने के लिए भी हमें सैकडों Lines की Coding करनी पडेगी।

जबकि ये Codes किसी भी Form के लिए हमेंशा Common होते हैं और इन्हें बार-बार न लिखना पडे, इसीलिए IDEs हमें विभिन्न GUI Controls से सम्बंधित Codes Visually Create करके Available करवा देते हैं, जिन्हें हम Reuse करते हुए बडी ही आसानी से बिना एक भी Line का Code लिखे हुए GUI Program Create कर सकते हैं और इस उदाहरण में हम यही करेंगे।

तो चलिए, हम Visual Studio के माध्‍यम से एक Simple सा GUI Application Create करते हैं और इस बात को समझने की कोशिश करते हैं कि GUI Applications के लिए किसी Specific Situation में  .NET द्वारा Provided Asynchronous Programming Concepts को Use करना कितना जरूरी हो जाता है।

चूंकि हमें Visual Studio का प्रयोग करते हुए एक Window Based Program Create करना है, इसलिए सबसे पहले Visual Studio के “FILE => New => Project…” Option को Click करके एक नया Project Create करते हैं।

इस Option को Click करते ही हमारे सामने निम्नानुसार “New Project” नाम का एक Dialog Box Display होता है, जिसमें Visual Studio द्वारा Create किए जा सकने वाले विभिन्न प्रकार के Application Software दिखाई देते हैं:

Asynchronous Programming - C# GUI - Hindi

उपरोक्त चित्र में दर्शा, अनुसार सभी Information Fill करके जैसे ही हम “OK” Button पर Click करते हैं, हमारा Window Forms Program हमें निम्नानुसार दिखाई देने लगता है:

Asynchronous Programming - C# GUI - Hindi

जैसाकि हम इस चित्र में देख सकते हैं कि हमारे सामने Automatically एक Visual Form दिखाई देने लगता है और Left Side में हमें एक Toolbox दिखाई देता है, जहां से हम हमारे Form पर Place किए जाने वाले विभिन्न प्रकार के User Interface Controls को Click करके Form पर Drag and Drop तकनीक का प्रयोग करते हुए Accuracy के साथ Visually Design कर सकते हैं, जो कि Manual Coding द्वारा सम्भव नहीं।

साथ ही एक GUI Program के लिए जरूरी सभी Assembly References Automatically Set हो जाते हैं, जिन्हें हम इस चित्र के Right Side में दिखाई देने वाले “References” के रूप में देख सकते हैं।

इतना ही नहीं इस Form व इस Form पर Place किए जाने वाले विभिन्न Controls की बहुत सारी Properties को हम Design Time में Bottom Right में दिखाई देने वाले Properties Window द्वारा Directly Set कर सकते हैं। जिनके लिए हमें एक भी Line का Code लिखने की जरूरत नहीं है।

यानी IDE हमें किसी Program को Design, Code, Create, Manage, Compile, Run, Extend, Debug Deploy करने तक की सारी सुविधाऐं एक ही Single Software के रूप में Provide कर देता है, जिससे हम हमारा सारा ध्‍यान अपने Professional Software की जरूरतों को Develop करने पर लगा सकते हैं।

यानी हमें हमारे Program को Develop करने से सम्बंधित अन्‍य Technical बातों जैसे कि Command Prompt द्वारा Program को Compile करते समय Specify किए जाने वाले विभिन्न Flags को Specify करने, विभिन्न प्रकार के Path Setting करने, Deployment से सम्बंधित Tasks Perform करने जैसी बातों की चिन्ता करने की जरूरत नहीं होती।

हालांकि हमने इस पुस्तक के विभिन्न Console Programs को Notepad++ के माध्‍यम से बनाया है, ताकि हम C# की Working, Compiling Process आदि को Deeply समझ सकें। लेकिन यदि हम चाहें तो उपरोक्तानुसार Visual Studio IDE में दिखाई देने वाले “New Project” Window के “Console Application” Option को Select करके अपने सभी Programs को Visual Studio का प्रयोग करते हुए भी Create, Compile, RunDebug कर सकते हैं और Visual Studio हमें हमारे हर Console Mode Application के विभिन्न Codes लिखने के लिए समय-समय पर Popup व Tool-Tips के माध्‍यम से Help भी Provide करता है।

लेकिन फिर भी मेरी सलाह यही है कि सीखने के स्तर पर Program Create करने के लिए Notepad++ Editor व Program को Compile व Run करने के लिए Command Prompt या “Notepad++” में Use किया गया NppExec Tool ही सही है। हालांकि Professional Development के स्तर पर आपको Visual Studio या SharpDevelop IDE ही Use करना चाहिए।

नया Project बनाने के बाद दिखाई देने वाले Form पर हमें एक Text Box व एक Button Control Place करना होता है, जिसे हम Left Side में दिखाई देने वाले Toolbox में Text Box व Button Control पर Double Click करके अपने For पर Place कर सकते हैं और फिर जरूरत के अनुसार अपने Form पर Position कर सकते हैं।

हमने हमारे Form पर स्थित Text Box का नाम Visual Studio IDE के Right Side में दिखाई देने वाले Properties Window का निम्न चित्रानुसार प्रयोग करते हुए Change करके txtInput कर दिया है तथा Button का नाम btnCallMethod कर दिया है। इन्हीं नामों को हम हमारे Program Code में Use करके इन Controls को Reference करते हैं:

Asynchronous Programming - C# GUI - Hindi

चूंकि हम चाहते हैं कि जब हम हमारे btnCallMethod नाम के Button को Click करें, तो वह किसी Method को Call करे। इस जरूरत को पूरा करने के लिए हमें हमारे Form पर दिखाई देने वाले Button के Click Event के लिए एक Code Handler लिखना होगा।

Code Handler लिखने के लिए हमें इस Form पर दिखाई देने वाले Button पर Double Click करना होता है। Double Click करते ही Visual Studio हमे निम्न चित्रानुसार Design View से Code View में पहुंचा देता है:

Asynchronous Programming - C# GUI - Hindi

जैसाकि उपरोक्त चित्र में हम देख सकते हैं कि जैसे ही हम हमारे Form पर दिखाई देने वाले Button पर Double Click करते हैं, हम Code Window में पहुंच जाते हैं और हमारे Button के लिए Automatically एक Click Event को Handle करने वाला Handler Method Create हो जाता है तथा हमारा Cursor उस Handler में Code Accept करने के लिए Blink करने लगता है।

हालांकि हमने अभी तक एक भी Line का Code नहीं लिखा है, फिर भी Visual Studio Automatically एक GUI Window Form के लिए जरूरी सभी Namespaces को हमारे Program में using Directive के साथ Specify कर देता है, जिन्हें हम उपरोक्त चित्र में दिख सकते हैं।

इन सभी Namespaces के बावजूद हमें हमारे Program को Normal तरीके से Run करने के लिए System.Threading Namespace को Include करना है, जिसे हम निम्न Statement द्वारा Include करते हैं, क्योंकि हम हमारे Program में इस Namespace में Defined Thread.Sleep() Method को Use करेंगे:

using System.Threading;

अब हमें हमारे btnCallMethod_Click() नाम के Automatically Create होने वाले Click Event Handler में निम्न Code लिखना होता है:

	private void btnCallMethod_Click(object sender, EventArgs e)
	{
		this.Text = DoWork();
	}

	private string DoWork()
	{
		Thread.Sleep(10000);
		return "Done with work!";
	}

अब इस Window Based GUI Program को Run करने के लिए हमें “Ctrl+F5” Key Combination Use करके इस Program को Run करते हैं, तो हमें हमारा Program कुछ निम्नानुसार दिखाई देता है:

Asynchronous Programming - C# GUI - Hindi

जहां हम Textbox में कुछ Type कर सकते हैं और Button पर Click कर सकते हैं। लेकिन जैसे ही हम Button पर Click करते हैं, हम दस Seconds के लिए इस Form पर कुछ भी Interaction नहीं कर सकते। यानी 10 Seconds के लिए ये Form पूरी तरह से Hanged यानी User Interaction के लिए Block हो जाता है।

क्योंकि Button को Click करते ही btnCallMethod_Click() Method Call होता है और ये Method Internally DoWork() Method को Call करता है, जिसमें हमने निम्नानुसार Code लिखकर 10 Seconds के लिए इस Method को Block या Suspend कर दिया है:

Thread.Sleep(10000);

यानी DoWork() नाम का Method 10 Seconds तक के लिए पूरे Form के User Interface को Block कर देता है। यहां 10 Second के Wait के रूप में हम उस काम को पूरा होने में लगने वाले समय के बराबर मान सकते हैं, जो कि DoWork() करता है।

उदाहरण के लिए मानलो कि DoWork() किसी Internet से किसी File को Download करके उसकी Information को Return करता है, जिसमें 10 Seconds का समय लगता है, तो Form पर दिखाई देने वाले Button को Click करने पर जब तक वह File पूरी तरह से Download नहीं हो जाती, तब तक हम हमारे Form के किसी भी अन्‍य Control के साथ किसी तरह का Interaction नहीं कर सकते। यहां तक कि वह इस Form को Close या Terminate तक नहीं कर सकता।

स्थिति तब और भी खराब हो जाती है जब हमारा Computer Internet से Connected न हो। उस स्थिति में ये Method Internet से Required File को किसी भी तरह से Download नहीं कर सकता और हमारा Program अनन्त समय के लिए Block हो जाता है, जब तक कि हम स्वयं इसे Task Manager द्वारा Hard Terminate न करें। किसी GUI Program की इसी प्रकार की समस्याओं से बचने के लिए Asynchronous Programming Concept को Use किया जाता है। यानी इसी Program के Codes को अब हम यदि निम्नानुसार async/await Keywords का प्रयोग करते हुए फिर से Redesign Design करें:

	private async void btnCallMethod_Click(object sender, EventArgs e)
	{
		this.Text = await DoWork();
	}

	// See below for code walkthrough...
	private Task<string> DoWork()
	{
		return Task.Run(() => {
		Thread.Sleep(10000);
		return "Done with work!";
		});
	}

तो इस बार Form पर दिखाई देने वाले Button पर Click करने पर पहले की तरह Blocking की समस्या नहीं आती और हम Button को Click करने के बाद भी Textbox में Data Insert कर सकते हैं अथवा Form के साथ अन्‍य प्रकार का Interaction कर सकते हैं। क्योंकि अब जब DoWork() Method 10 Seconds के लिए Block होता है, तो वह एक अलग Task को अलग Thread में Block करता है न कि Main Thread को।

हालांकि हमने async/await के लिए पिछले कई Sections में कई Console Applications Create किए हैं। फिर भी एक GUI Form के आधार पर फिर से async/await की Working को समझने की कोशिश करते हैं।

इस Modified Program Code में हमने Button के Click Event Handler को async Keyword के साथ Specify करके CLR को इस बात का Instruction दिया है कि इस Method में यदि कोई Blocking Code हो, तो भी इस Method को Non-Blocking Manner में ही Execute किया जाए और Blocking Code को Handle करने के लिए अलग Thread या Task Create किया जाए।

साथ ही हमने उस Blocking Code यानी DoWork() Method को निम्नानुसार तरीके से await Keyword के साथ Specify किया है, जो कि हमारे Program के Main Thread को 10 Seconds के लिए Block कर रहा है:

this.Text = await DoWork();

हमने इस Statement में DoWork() Method के साथ await Keyword का प्रयोग इसलिए किया है, ताकि हम CLR को इस बात का Instruction दे सकें कि यही वह Method है, जो Main Thread को Block कर रहा है और इसी Method के Complete होने के लिए Main Thread को Wait करवाने की जरूरत नहीं है।

अत: यदि हम अपने Method के साथ async Keyword Use करें, लेकिन इस DoWork() Method के साथ await Keyword Specify न करें, तब भी हमारा Main Thread Block होगा, क्योंकि हमने CLR को इस बात का Instruction ही नहीं दिया कि कौनसा Code Blocking कर रहा है।

इस Program में DoWork() Method को हमने पिछले Example की तुलना में काफी Change किया है। हमने ऐसा इसलिए किया है क्योंकि हम await के साथ उसी Method को Specify कर सकते हैं जो कि Asynchronous Task को Represent करता है और हमारा Modified DoWork() Method हालांकि एक String “Done with work!” ही Return कर रहा है, लेकिन इस String को एक Task Object की तरह Return कर रहा है और Task Object Create करने के लिए हमने निम्नानुसार तरीके से Task.Run() Method Define किया है तथा सारे Waiting Codes को इस Task Method में Specify किया है जो कि एक Anonymous Method का Lambda Expression है:

return Task.Run(() => {
Thread.Sleep(10000);
return “Done with work!”;
});

हालांकि यदि हम चाहें तो इसी Lambda Expression Statement को निम्नानुसार Anonymous Method के रूप में भी Specify कर सकते हैं:

return Task.Run(delegate(){
Thread.Sleep(10000);
return “Done with work!”;
});

जैसाकि हमने इसी Chapter के पिछले Sections में पहले भी Discuss किया है कि Task.Run() Method Parameter के रूप में Func<> या Action<> Delegate Type का Object Accept करता है, जो कि उस Executable Task को Represent करता है, जिसे Asynchronously Callback करना है और उपरोक्त Code में Specified Lambda Expression या Anonymous Method यही काम कर रहा है।

यदि हम सरल शब्दों में समझें तो उपरोक्त DoWork() Method यही कह रहा है कि जब DoWork() को Call किया जाएगा, तो ये एक Task Run करेगा, जो कि एक ऐसा काम करेगा, जिसे पूरा होने में 10 Seconds का समय लगेगा और जब ये Task पूरा हो जाएगा, तो वह Task एक String Value Return करेगा, जिसे DoWork() Method एक नए Task<string> Object में Store करके Caller को Return कर देगा।”

इस तरह से हम समझ सकते हैं कि await Token हमेंशा एक ऐसा Method ही Specify करता है, जो कि Task Type का Object Return करता हो। इसलिए जब हम await को उपरोक्तानुसार किसी GUI Application में Asynchronous Functionality प्राप्त करने के लिए Use करते हैं, तो await के साथ Specify किया जाने वाला Method इसी तरह का Define करना होता है, जो Return Value के रूप में एक Task Object Return करे। जबकि उस Task Object में वह Actual Value होती है, जिसे Caller को Return करना होता है।

यहां एक ध्‍यान देने वाली बात ये भी है कि यदि हम DoWork() Method को उपरोक्तानुसार Modify करके ऐसा बना देते हैं, जो कि एक Task Object Return करता है, लेकिन DoWork() Method को Invoke करने से पहले await Keyword को Use नहीं करते, तो Visual Studio हमें निम्न चित्रानुसार Type Mismatch का एक Error Show करता है:

Asynchronous Programming - C# GUI - Hindi

इस तरह से हम बडी ही आसानी से async/await Keywords का प्रयोग करते हुए एक ऐसा GUI Program Create कर सकते हैं, जहां कोई भी Time Consuming Heavy Task Perform करने वाला Method Program के Main Thread को Block नहीं करता।

Microsoft हमें ऐसी सुविधा Provide करता है कि हम किसी भी ऐसे Method के साथ await Token को Use कर सकते हैं, जो कि Task Type का Object Create करता हो और Microsoft किसी भी Task Return करने वाले Method को await Keyword के साथ Specify करने के लिए ही Recommend करता है। यानी हमें हर उस Method को await Keyword के साथ ही Invoke करना चाहिए, जो कि Task Object Return करता हो।

साथ ही Microsoft हमें किसी भी Asynchronously Run होने वाले Method के नाम के साथ Suffix के रूप में “Async” शब्द को Add करने की सलाह देता है, ताकि हम किसी Method के नाम को देखते ही आसानी से ये समझ सकें कि उस Method को किसी async Method में await Keyword के साथ Use करना है। इसलिए यदि हम इस Convention को Follow करते हुए अपने पिछले Program Code को फिर से लिखें, तो हमारा Program Code कुछ निम्नानुसार होगा:

	private async void btnCallMethod_Click(object sender, EventArgs e)
	{
		this.Text = await DoWork();
	}

	// See below for code walkthrough...
	private Task<string> DoWorkAsync()
	{
		return Task.Run(() => {
		Thread.Sleep(10000);
		return "Done with work!";
		});
	}

इस प्रकार से हमने इस Chapter में Multi-Threading Concept को Detail से समझने की कोशिश की। हालांकि हमने इस Chapter में Multi-Threading व Parallel Programming से सम्बंधित .NET Platform Provided सभी Features के बारे में Discuss नहीं किया है।

क्योंकि Multi-Threading व Parallel Programming अपने आप में काफी बडा Subject है और केवल इनसे सम्बंधित सभी Features को Detail से समझने के लिए हमें एक अलग पुस्तक लिखने की जरूरत पडेगी।

लेकिन फिर भी इस Chapter में इन विषयों पर जितना Discussion किया गया है, उसके आधार पर आप आसानी से .NET के Multi-Threading व Parallel Programming से सम्बंधित .NET Framework Provided Features को उपयोग में ले सकते हैं और नए Concepts को सीख सकते हैं।

Difference between Synchronous and Asynchronous
C# Preprocessor Directives

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

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

BUY NOW GET DEMO REVIEWS