डिफ़ॉल्ट रूप से C # लागू करने के तरीके गैर-आभासी क्यों हैं?


105

जावा के विपरीत, C # तरीकों को डिफ़ॉल्ट रूप से गैर-आभासी कार्यों के रूप में क्यों मानता है? क्या अन्य संभावित परिणामों के बजाय प्रदर्शन का मुद्दा होने की अधिक संभावना है?

मुझे एंडर्स हेजलबर्ग के एक पैराग्राफ को पढ़ने के बारे में याद दिलाया गया है, जिसमें मौजूदा आर्किटेक्चर के कई फायदे सामने आ रहे हैं। लेकिन, दुष्प्रभाव के बारे में क्या? क्या डिफ़ॉल्ट रूप से गैर-आभासी तरीकों का होना वास्तव में एक अच्छा व्यापार है?


1
जवाब है कि प्रदर्शन के कारणों का उल्लेख तथ्य यह है कि सी # संकलक ज्यादातर के लिए विधि कॉल संकलित की अनदेखी callvirt और न फोन । यही कारण है कि C # में ऐसा तरीका संभव नहीं है जो thisसंदर्भ के अशक्त होने पर अलग तरह से व्यवहार करता हो । अधिक जानकारी के लिए यहां देखें ।
एंडी

सच! कॉल आईएल निर्देश ज्यादातर स्थिर तरीकों से किए गए कॉल के लिए है।
RBT

1
सी # के आर्किटेक्ट एंडर्स हेजलबर्ग के विचार यहां और यहां हैं
RBT

जवाबों:


98

इनहेरिटेंस के लिए कक्षाएं डिज़ाइन की जानी चाहिए ताकि वे इसका लाभ उठा सकें। virtualडिफ़ॉल्ट रूप से विधियां होने का अर्थ है कि कक्षा में प्रत्येक फ़ंक्शन को प्लग आउट किया जा सकता है और दूसरे द्वारा प्रतिस्थापित किया जा सकता है, जो वास्तव में अच्छी बात नहीं है। बहुत से लोग यह भी मानते हैं कि कक्षाएं sealedडिफ़ॉल्ट रूप से होनी चाहिए थीं।

virtualविधियों में थोड़ा सा प्रदर्शन निहितार्थ भी हो सकता है। हालांकि, यह प्राथमिक कारण होने की संभावना नहीं है।


7
व्यक्तिगत रूप से, मुझे संदेह है कि प्रदर्शन के बारे में। वर्चुअल फ़ंक्शंस के लिए भी, कंपाइलर यह पता लगाने में सक्षम है कि IL कोड में callvirtएक साधारण call(या JITting के दौरान और भी नीचे की तरफ) की जगह कहां से आ सकती है। जावा हॉटस्पॉट भी यही करता है। शेष उत्तर हाजिर है।
कोनराड रुडोल्फ

5
मैं मानता हूं कि प्रदर्शन सबसे आगे नहीं है, लेकिन इसके प्रदर्शन के निहितार्थ भी हो सकते हैं। यह शायद बहुत छोटा है। निश्चित रूप से, यह इस पसंद का कारण नहीं है , हालांकि। बस उसी का जिक्र करना चाहता था।
मेहरदाद अफशरी

71
सीलबंद कक्षाएं मुझे बच्चों को पंच करना चाहती हैं।
21

6
@mxmissile: मुझे भी, लेकिन अच्छा API डिज़ाइन वैसे भी कठिन है। मुझे लगता है कि सीलबंद डिफ़ॉल्ट रूप से आपके पास कोड के लिए सही अर्थ है। अधिक बार नहीं, आपकी टीम के अन्य प्रोग्रामर ने विरासत पर विचार नहीं किया, जब उन्होंने उस वर्ग को लागू किया, जिसकी आपको आवश्यकता थी, और सीलबंद डिफ़ॉल्ट रूप से उस संदेश को काफी अच्छी तरह से व्यक्त करेगा।
रोमन स्टार्कोव

49
बहुत से लोग मनोरोगी भी होते हैं, इसका मतलब यह नहीं है कि हमें उन्हें सुनना चाहिए। वर्चुअल को सी # में डिफ़ॉल्ट होना चाहिए, कार्यान्वयन को बदलने या इसे फिर से लिखने के बिना कोड को एक्स्टेंसिबल होना चाहिए। जो लोग अपने API को ओवरप्रोटेक्ट करते हैं, वे अक्सर मृत या आधे-अधूरे एपीआई का इस्तेमाल करते हैं, जो किसी के दुरुपयोग करने के परिदृश्य से कहीं अधिक खराब है।
क्रिस निकोला

89

मुझे आश्चर्य है कि यहां ऐसी सहमति बनती दिख रही है कि गैर-वर्चुअल-बाय-डिफ़ॉल्ट चीजें करने का सही तरीका है। मैं दूसरे पर उतरने जा रहा हूं - मुझे लगता है कि व्यावहारिक - बाड़ की तरफ।

अधिकांश औचित्य मेरे लिए पुराने की तरह पढ़ते हैं "यदि हम आपको शक्ति देते हैं तो आप स्वयं को चोट पहुंचा सकते हैं" तर्क। प्रोग्रामर से ?!

यह मुझे ऐसा लगता है जैसे कोडर जो पर्याप्त नहीं जानता था (या उसके पास पर्याप्त समय है) विरासत के लिए अपनी लाइब्रेरी को डिजाइन करने के लिए और / या एक्स्टेंसिबिलिटी कोडर है जो वास्तव में लाइब्रेरी का निर्माण करता है मुझे ठीक करने या ट्वीक करने की संभावना है - बिल्कुल पुस्तकालय जहां ओवरराइड करने की क्षमता सबसे उपयोगी होगी।

जितनी बार मुझे बदसूरत लिखना पड़ा है, उतने ही हताश काम-कोड के बारे में (या उपयोग को छोड़ना और अपना वैकल्पिक समाधान रोल करना है) क्योंकि मैं अब तक ओवरराइड नहीं कर सकता, मैंने जितनी बार काट ली है, उतनी बार आउटवेग करता है ( जावा में जैसे) ओवरराइड करके जहाँ डिज़ाइनर ने शायद नहीं माना होगा।

गैर-आभासी-बाय-डिफॉल्ट मेरे जीवन को कठिन बना देता है।

अपडेट करें: यह बताया गया है [काफी सही ढंग से] कि मैं वास्तव में इस सवाल का जवाब नहीं दिया। इसलिए - और देर से होने के लिए माफी के साथ ...।

मुझे लगता है कि थोड़े-थोड़े लिखने में सक्षम होना चाहता था जैसे "सी # लागू करने के तरीके गैर-आभासी के रूप में डिफ़ॉल्ट रूप से क्योंकि एक बुरा निर्णय किया गया था जो प्रोग्रामर की तुलना में अधिक मूल्यवान प्रोग्राम"। (मुझे लगता है कि इस सवाल के कुछ अन्य उत्तरों के आधार पर कुछ हद तक उचित ठहराया जा सकता है - जैसे प्रदर्शन (समय से पहले अनुकूलन, किसी को?), या कक्षाओं के व्यवहार की गारंटी।)

हालांकि, मुझे लगता है कि मैं सिर्फ अपनी राय बता रहा हूं और यह निश्चित जवाब नहीं है कि स्टैक ओवरफ्लो इच्छाएं हैं। निश्चित रूप से, मैंने सोचा, उच्चतम स्तर पर निश्चित (लेकिन अप्रभावी) उत्तर है:

वे डिफ़ॉल्ट रूप से गैर-आभासी हैं क्योंकि भाषा-डिजाइनरों के पास बनाने का निर्णय था और यही उन्होंने चुना।

अब मैं सटीक कारण का अनुमान लगाता हूं कि उन्होंने यह निर्णय लिया था कि हम कभी नहीं करेंगे .... ओह, रुको! एक वार्तालाप की प्रतिलेख!

इसलिए ऐसा लगता है कि एपीआई के ओवरराइडिंग के खतरों के बारे में यहां जवाब और टिप्पणियां और उत्तराधिकार के लिए स्पष्ट रूप से डिजाइन करने की आवश्यकता सही रास्ते पर है, लेकिन सभी एक महत्वपूर्ण अस्थायी पहलू याद कर रहे हैं: एंडर्स की मुख्य चिंता एक वर्ग या एपीआई के निहित को बनाए रखने के बारे में थी। अनुबंध संस्करणों में । और मुझे लगता है कि वह वास्तव में .Net / C # प्लेटफॉर्म को कोड के तहत बदलने की अनुमति देने के बारे में अधिक चिंतित है, बजाय प्लेटफॉर्म के शीर्ष पर उपयोगकर्ता-कोड बदलने के बारे में चिंतित होने के। (और उसका "व्यावहारिक" दृष्टिकोण मेरा बिल्कुल विपरीत है क्योंकि वह दूसरी तरफ से देख रहा है।)

(लेकिन क्या वे वर्चुअल-बाय-डिफॉल्ट उठा सकते हैं और फिर कोडबेस के माध्यम से "फाइनल" पेप्पर्ड कर सकते हैं; शायद यह बिल्कुल समान नहीं है .. और एंडर्स स्पष्ट रूप से मुझसे ज्यादा स्मार्ट हैं इसलिए मैं इसे झूठ बोलने जा रहा हूं। "


2
पूर्ण समर्थन। किसी थर्ड पार्टी एपी का उपभोग करने और कुछ व्यवहार को ओवरराइड करने में सक्षम नहीं होने पर बहुत निराशा होती है।
एंडी

3
मैं उत्साह से सहमत हूं। यदि आप एक एपीआई प्रकाशित करना चाहते हैं (चाहे आंतरिक रूप से किसी कंपनी में या बाहरी दुनिया के लिए), तो आप वास्तव में यह सुनिश्चित कर सकते हैं कि आपका कोड वंशानुक्रम के लिए डिज़ाइन किया गया है। यदि आप इसे कई लोगों द्वारा उपयोग किए जाने के लिए प्रकाशित कर रहे हैं, तो आपको एपीआई को अच्छा और पॉलिश करना चाहिए। अच्छे समग्र डिजाइन (अच्छी सामग्री, स्पष्ट उपयोग के मामले, परीक्षण, प्रलेखन) के लिए आवश्यक प्रयास की तुलना में, विरासत के लिए डिजाइन करना वास्तव में बहुत बुरा नहीं है। और यदि आप प्रकाशित नहीं कर रहे हैं, तो वर्चुअल-बाय-डिफ़ॉल्ट कम समय लेने वाला है, और आप हमेशा उन मामलों के छोटे हिस्से को ठीक कर सकते हैं जहां यह समस्याग्रस्त है।
गुरुत्वाकर्षण

2
अब अगर विजुअल स्टूडियो में केवल एक संपादक की सुविधा थी, तो सभी तरीकों / गुणों को स्वचालित रूप से टैग करने के लिए virtual... विजुअल स्टूडियो ऐड-इन किसी भी?
केविनर्पपे

1
जबकि मैं तहे दिल से आपसे सहमत हूँ, यह बिल्कुल जवाब नहीं है।
क्रिस मॉर्गन

2
आधुनिक विकास प्रथाओं जैसे कि निर्भरता इंजेक्शन, मॉकिंग फ्रेमवर्क और ओआरएम को ध्यान में रखते हुए, यह स्पष्ट लगता है कि हमारे सी # डिजाइनरों ने निशान को थोड़ा याद किया। जब आप डिफ़ॉल्ट रूप से किसी संपत्ति को ओवरराइड नहीं कर सकते हैं तो निर्भरता का परीक्षण करना बहुत निराशाजनक है।
जेरेमी होलोवाक

17

क्योंकि यह भूल जाना बहुत आसान है कि एक विधि को ओवरराइड किया जा सकता है और उसके लिए डिज़ाइन नहीं किया जा सकता है। C # आपको वर्चुअल बनाने से पहले आपको सोचता है। मुझे लगता है कि यह एक महान डिजाइन निर्णय है। कुछ लोगों (जैसे जॉन स्कीट) ने यहां तक ​​कहा कि कक्षाओं को डिफ़ॉल्ट रूप से सील किया जाना चाहिए।


12

दूसरों ने जो कहा, उसे संक्षेप में बताने के लिए, कुछ कारण हैं:

1- C # में, वाक्य रचना और शब्दार्थ में कई चीजें हैं जो सीधे C ++ से आती हैं। तथ्य यह है कि सी + + में डिफ़ॉल्ट रूप से आभासी नहीं जहां तरीकों ने प्रभावित किया।

2- प्रत्येक विधि को डिफ़ॉल्ट रूप से आभासी रखना एक प्रदर्शन चिंता का विषय है क्योंकि प्रत्येक विधि कॉल को ऑब्जेक्ट की वर्चुअल टेबल का उपयोग करना चाहिए। इसके अलावा, यह दृढ़ता से इनलाइन विधियों में जस्ट-इन-टाइम संकलक की क्षमता को सीमित करता है और अन्य प्रकार के अनुकूलन करता है।

3- सबसे महत्वपूर्ण, अगर तरीके डिफ़ॉल्ट रूप से आभासी नहीं हैं, तो आप अपनी कक्षाओं के व्यवहार की गारंटी दे सकते हैं। जब वे डिफ़ॉल्ट रूप से आभासी होते हैं, जैसे कि जावा में, आप यह भी गारंटी नहीं दे सकते हैं कि एक साधारण गेट्टर विधि जैसा कि इरादा है, क्योंकि यह एक व्युत्पन्न वर्ग में कुछ भी करने के लिए ओवरराइड किया जा सकता है (बेशक, आप कर सकते हैं, और करना चाहिए विधि और / या वर्ग अंतिम)।

एक आश्चर्य हो सकता है, जैसा कि ज़िफरे ने उल्लेख किया है, क्यों सी # भाषा एक कदम भी आगे नहीं बढ़ी और कक्षाओं को डिफ़ॉल्ट रूप से सील कर दिया। यह कार्यान्वयन विरासत की समस्याओं के बारे में पूरी बहस का हिस्सा है, जो एक बहुत ही दिलचस्प विषय है।


1
मैं डिफ़ॉल्ट रूप से सील की गई कक्षाओं को प्राथमिकता देता। तब यह सुसंगत होगा।
एंडी

9

C # C ++ (और अधिक) से प्रभावित है। C ++ डिफ़ॉल्ट रूप से डायनेमिक प्रेषण (वर्चुअल फ़ंक्शंस) को सक्षम नहीं करता है। इसके लिए एक (अच्छा?) तर्क यह सवाल है: "आप कितनी बार उन कक्षाओं को लागू करते हैं जो एक वर्ग hiearchy के सदस्य हैं?"। डिफ़ॉल्ट रूप से डायनेमिक प्रेषण को सक्षम करने से बचने का एक अन्य कारण मेमोरी फ़ुटप्रिंट है। वर्चुअल पॉइंटर (vpointer) के बिना वर्चुअल टेबल की ओर इशारा करते हुए एक वर्ग, बिना बाध्यकारी बाइंडिंग के साथ संबंधित वर्ग से छोटा है।

प्रदर्शन का मुद्दा "हां" या "नहीं" कहना इतना आसान नहीं है। इसका कारण जस्ट इन टाइम (JIT) संकलन है जो C # में रन टाइम ऑप्टिमाइज़ेशन है।

इसी तरह का एक और सवाल, " वर्चुअल कॉल की गति .. "


मुझे थोड़ा संदेह है कि JIT कंपाइलर के कारण वर्चुअल तरीकों का C # के लिए प्रदर्शन निहितार्थ है। यह उस क्षेत्र में से एक है जहां जेआईटी ऑफ़लाइन संकलन से बेहतर हो सकता है, क्योंकि वे इनलाइन फ़ंक्शन कॉल कर सकते हैं जो रनटाइम से पहले "अज्ञात" हैं
डेविड कूर्नापेउ

1
वास्तव में, मुझे लगता है कि यह जावा से C ++ से अधिक प्रभावित है जो कि डिफ़ॉल्ट रूप से करता है।
मेहरदाद अफश्री

5

सरल कारण प्रदर्शन लागत के अलावा डिजाइन और रखरखाव लागत है। एक गैर-आभासी विधि की तुलना में एक आभासी विधि की अतिरिक्त लागत होती है क्योंकि कक्षा के डिजाइनर को उस योजना की योजना बनानी चाहिए जब किसी अन्य वर्ग द्वारा विधि को ओवरराइड किया जाता है। यह एक बड़ा प्रभाव है यदि आप आंतरिक स्थिति को अपडेट करने के लिए किसी विशेष विधि की अपेक्षा करते हैं या एक विशेष व्यवहार करते हैं। अब आपको इस बात की योजना बनानी होगी कि जब एक व्युत्पन्न वर्ग उस व्यवहार को बदलता है तो क्या होता है। उस स्थिति में विश्वसनीय कोड लिखना बहुत कठिन है।

एक गैर-आभासी पद्धति के साथ आपका कुल नियंत्रण होता है। जो कुछ भी गलत होता है वह मूल लेखक की गलती है। कोड के बारे में तर्क करना बहुत आसान है।


यह एक बहुत पुरानी पोस्ट है, लेकिन एक नौसिखिया के लिए जो अपना पहला प्रोजेक्ट लिख रहा है, मैं कोड लिखने के अनजाने / अज्ञात परिणामों पर लगातार झल्लाहट करता हूं। यह जानकर बहुत सुकून मिलता है कि गैर-आभासी तरीके पूरी तरह से मेरी गलती हैं।
trevorc

2

अगर सभी C # तरीके वर्चुअल होते तो vtbl ज्यादा बड़ा होता।

यदि क्लास में वर्चुअल तरीके परिभाषित हैं तो C # ऑब्जेक्ट में केवल वर्चुअल तरीके हैं। यह सच है कि सभी ऑब्जेक्ट्स में टाइप जानकारी होती है जिसमें vtbl समतुल्य होता है, लेकिन यदि कोई वर्चुअल तरीके परिभाषित नहीं किए जाते हैं, तो केवल आधार ऑब्जेक्ट विधियाँ मौजूद होंगी।

@ टॉम हॉकिन: यह कहना शायद अधिक सटीक है कि C ++, C # और Java सभी भाषाओं के C परिवार से हैं :)


1
विटेबुल के लिए बहुत बड़ा होना एक समस्या क्यों होगी? प्रति वर्ग केवल 1 व्यवहार्य है (और प्रति उदाहरण नहीं), इसलिए इसके आकार में बहुत अधिक अंतर नहीं है।
गुरुत्वाकर्षण

1

एक पर्ल बैकग्राउंड से आ रहा है, मुझे लगता है कि C # हर उस डेवलपर का डूम सील कर दिया गया है जो शायद बेस क्लास 'के व्यवहार को बदलना और संशोधित करना चाहता था, जो नई क्लास के सभी यूजर्स को सीन के पीछे जाने के लिए मजबूर किए बिना एक नॉन वर्चुअल तरीका अपनाता है। विवरण।

सूची वर्ग 'विधि जोड़ें पर विचार करें। जब भी कोई विशेष सूची 'जोड़ा' जाता है, तो एक डेवलपर कई संभावित डेटाबेस में से एक को अपडेट करना चाहता था तो क्या होगा? यदि 'ऐड' डिफ़ॉल्ट रूप से आभासी था, तो डेवलपर एक 'बैकलेडलिस्ट' वर्ग विकसित कर सकता है, जो सभी क्लाइंट कोड को यह जानने के लिए कि 'रेगुलर' लिस्ट 'के बजाए' बैकडलिस्ट 'के लिए मजबूर किए बिना' ऐड 'पद्धति को ओवररोड कर सकता है। सभी व्यावहारिक उद्देश्यों के लिए 'BackedList' को ग्राहक कोड से सिर्फ एक अन्य 'सूची' के रूप में देखा जा सकता है।

यह एक बड़े मुख्य वर्ग के दृष्टिकोण से समझ में आता है जो एक या अधिक सूची घटकों तक पहुंच प्रदान कर सकता है जो स्वयं एक डेटाबेस में एक या अधिक स्कीमा द्वारा समर्थित हैं। यह देखते हुए कि C # विधियां डिफ़ॉल्ट रूप से आभासी नहीं हैं, मुख्य वर्ग द्वारा प्रदान की गई सूची एक साधारण IEnumerable या ICollection या यहां तक ​​कि एक सूची उदाहरण नहीं हो सकती है, बल्कि नए संस्करण को सुनिश्चित करने के लिए ग्राहक को 'BackedList' के रूप में विज्ञापित किया जाना चाहिए। सही स्कीमा को अद्यतन करने के लिए 'ऐड' ऑपरेशन को कहा जाता है।


सच है, डिफ़ॉल्ट रूप से आभासी तरीके काम को आसान बनाते हैं, लेकिन क्या यह समझ में आता है? जीवन को आसान बनाने के लिए कई चीजें हैं जिन्हें आप नियोजित कर सकते हैं। सिर्फ कक्षाओं में सार्वजनिक क्षेत्र क्यों नहीं? इसके व्यवहार को बदलना बहुत आसान है। मेरी राय में भाषा में सब कुछ सख्ती से संक्षिप्त, कठोर और डिफ़ॉल्ट रूप से बदलने के लिए लचीला होना चाहिए। आवश्यकता होने पर ही इसे बदलें। बस lil 'दार्शनिक abt डिजाइन निर्णय। जारी है ..
नवफाल

... वर्तमान विषय पर बात करने के लिए, इनहेरिटेंस मॉडल का उपयोग केवल तभी किया जाना चाहिए जब Bवह हो A। अगर Bकुछ अलग करने की आवश्यकता है Aतो उसकी नहीं A। मेरा मानना ​​है कि भाषा में एक क्षमता के रूप में ओवरराइडिंग एक डिजाइन दोष है। यदि आपको एक अलग Addविधि की आवश्यकता है , तो आपका संग्रह वर्ग ए नहीं है List। यह बताने की कोशिश की जा रही है कि यह गलत है। यहाँ सही दृष्टिकोण रचना है (और फ़ेकिंग नहीं)। यह सच है कि संपूर्ण ढांचा ओवरराइडिंग क्षमताओं पर बनाया गया है, लेकिन मुझे यह पसंद नहीं है।
नवफाल

मुझे लगता है कि मैं आपकी बात को समझता हूं, लेकिन प्रदान किए गए ठोस उदाहरण पर: 'बैकलिस्ट' केवल इंटरफ़ेस 'IList' को लागू कर सकता है और क्लाइंट केवल इंटरफ़ेस के बारे में जानता है। सही? क्या मैं कुछ भूल रहा हूँ? हालाँकि मैं समझता हूँ कि आप जिस व्यापक बिंदु को बनाने की कोशिश कर रहे हैं।
वित्रस

0

यह निश्चित रूप से प्रदर्शन का मुद्दा नहीं है। सन के जावा दुभाषिया प्रेषण ( invokevirtualबाइटकोड) के लिए उसी कोड का उपयोग करता है और हॉटस्पॉट ठीक उसी कोड को उत्पन्न करता है finalया नहीं। मेरा मानना ​​है कि सभी C # ऑब्जेक्ट्स (लेकिन स्ट्रक्चर्स नहीं) में वर्चुअल तरीके हैं, इसलिए आपको हमेशा जरूरत पड़ने वाली हैvtbl / रनटाइम क्लास आइडेंटिफिकेशन की जरूरत है। C # "जावा जैसी भाषाओं" की एक बोली है। सुझाव है कि यह C ++ से आता है पूरी तरह से ईमानदार नहीं है।

एक विचार है कि आपको "विरासत के लिए डिज़ाइन करना चाहिए या फिर इसे प्रतिबंधित करना चाहिए"। जो एक महान विचार की तरह लगता है ठीक उसी समय जब आपके पास एक त्वरित फिक्स में डालने के लिए एक गंभीर व्यावसायिक मामला है। शायद उस कोड से विरासत में मिला जिसे आप नियंत्रित नहीं करते हैं।


हॉटस्पॉट को उस अनुकूलन को करने के लिए बड़ी लंबाई में जाने के लिए मजबूर किया जाता है, ठीक है क्योंकि सभी विधियां डिफ़ॉल्ट रूप से आभासी हैं, जिसका एक बड़ा प्रदर्शन प्रभाव पड़ता है। CoreCLR जबकि बहुत आसान जा रहा है इसी तरह के प्रदर्शन को प्राप्त करने में सक्षम है
येर हेल्बरस्टाड

@YairHalberstadt Hotspot को कई अन्य कारणों से संकलित कोड को वापस करने में सक्षम होने की आवश्यकता है। यह वर्ष है क्योंकि मैंने स्रोत को देखा है, लेकिन finalप्रभावी और प्रभावी finalतरीके के बीच का अंतर तुच्छ है। यह भी ध्यान देने योग्य है कि यह बिफोरफिक इनलाइनिंग कर सकता है, जो कि दो अलग-अलग कार्यान्वयनों के साथ इनलाइन विधियां हैं।
टॉम हॉल्टिन -
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.