माइक्रोसॉफ्ट एक्सेस में संस्करण नियंत्रण के एक मजबूत समर्थक के रूप में, मुझे वीबीए विकास पर्यावरण के साथ मेरी सबसे बड़ी शिकायत के बारे में बात करने की ज़रूरत है:पहचानकर्ताओं के स्वचालित "पुनरावृत्ति"। इसे स्टैकओवरफ़्लो पर इस "सुविधा" के बारे में एक प्रश्न के मेरे उत्तर के विस्तार के रूप में सोचें।
मैं इस लेख को दो भागों में देखने जा रहा हूँ। भाग 1 में, मैं विकास पर्यावरण के व्यवहार को परिभाषित करूंगा। भाग 2 में, मैं अपने सिद्धांत पर चर्चा करूँगा कि यह इस तरह क्यों काम करता है।
भाग 1:व्यवहार को परिभाषित करना
यदि आपने VBA में कोड लिखने में बहुत समय बिताया है, तो मुझे यकीन है कि आपने इस "सुविधा" पर ध्यान दिया होगा। जैसा कि आप पहचानकर्ताओं में टाइप कर रहे हैं - चर, फ़ंक्शन नाम, एनम, आदि - आप देख सकते हैं कि आईडीई स्वचालित रूप से इन पहचानकर्ताओं के आवरण को बदल देता है। उदाहरण के लिए, आप सभी लोअरकेस अक्षरों में एक वैरिएबल नाम टाइप कर सकते हैं, लेकिन जैसे ही आप एक नई लाइन में जाते हैं, आपके वेरिएबल का पहला अक्षर अचानक अपरकेस में बदल जाता है।
पहली बार जब आप इसे देखते हैं तो यह झकझोरने वाला हो सकता है। जैसा कि आप प्रोग्रामिंग जारी रखते हैं, आईडीई आपके मामले को यादृच्छिक रूप से बदलना जारी रखता है। लेकिन, यदि आप IDE में पर्याप्त समय बिताते हैं, तो अंततः पैटर्न स्वयं प्रकट हो जाता है।
आपको अपने जीवन के दस से अधिक वर्ष बिताने से बचाने के लिए पैटर्न के आपके सामने प्रकट होने की प्रतीक्षा में, मैं अब उस पैटर्न का वर्णन करूंगा जैसा कि मैं इसे समझ गया हूं। मेरी जानकारी में, Microsoft ने कभी भी इस व्यवहार का आधिकारिक रूप से दस्तावेज़ीकरण नहीं किया है।
- सभी स्वचालित केस परिवर्तन VBA प्रोजेक्ट के लिए वैश्विक हैं।
- जब भी निम्न में से किसी भी प्रकार के पहचानकर्ता की घोषणा पंक्ति बदली जाती है, उसी नाम वाले प्रत्येक अन्य पहचानकर्ता का आवरण भी बदल जाता है:
- उप नाम
- फ़ंक्शन का नाम
- नाम टाइप करें
- एनम नाम
- परिवर्तनीय नाम
- निरंतर नाम
- संपत्ति का नाम
- जब भी किसी एनम आइटम का नाम कोड में कहीं भी बदला जाता है, तो एनम आइटम नाम का केसिंग हर जगह मिलान करने के लिए अपडेट किया जाता है।
आइए अब इनमें से प्रत्येक व्यवहार के बारे में थोड़ा और विस्तार से बात करते हैं।
वैश्विक परिवर्तन
जैसा कि मैंने ऊपर लिखा है, पहचानकर्ता मामले में परिवर्तन एक वीबीए परियोजना के लिए वैश्विक हैं। दूसरे शब्दों में, पहचानकर्ताओं के मामले को बदलते समय VBA IDE पूरी तरह से दायरे की उपेक्षा करता है।
उदाहरण के लिए, मान लें कि आपके पास AccountIsActive . नामक एक निजी फ़ंक्शन है एक मानक मॉड्यूल में। अब, उसी प्रोजेक्ट में कहीं और क्लास मॉड्यूल की कल्पना करें। क्लास मॉड्यूल में एक निजी संपत्ति प्राप्त करने की प्रक्रिया है। उस संपत्ति के अंदर प्रक्रिया प्राप्त करने की प्रक्रिया accountIsActive . नामक एक स्थानीय चर है . जैसे ही आप लाइन टाइप करते हैं Dim accountIsActive As Boolean
VBA IDE में और एक नई लाइन पर जाएँ, फ़ंक्शन खाता सक्रिय है जिसे हमने इसके अपने मानक मॉड्यूल में अलग से परिभाषित किया है, इसकी घोषणा लाइन को Private Function accountIsActive()
में बदल दिया गया है। इस वर्ग मॉड्यूल के अंदर स्थानीय चर से मिलान करने के लिए।
यह एक कौर है, इसलिए मैं इसे कोड में बेहतर तरीके से प्रदर्शित करता हूं।
चरण 1:AccountIsActive फ़ंक्शन को परिभाषित करें
'--== Module1 ==--
Private Function AccountIsActive() As Boolean
End Function
चरण 2:अलग-अलग दायरे में accountIsActive स्थानीय चर घोषित करें
'--== Class1 ==--
Private Sub Foo()
Dim accountIsACTIVE As Boolean
End Sub
चरण 3:VBA IDE...आपने क्या किया?!?!
'--== Module1 ==--
Private Function accountIsACTIVE() As Boolean
End Function
VBA केस-विलोपन की गैर-भेदभाव नीति
केवल दायरे को अनदेखा करने के लिए सामग्री नहीं, वीबीए भी आवरण स्थिरता लागू करने की अपनी खोज में पहचानकर्ताओं के बीच मतभेदों को अनदेखा करता है। दूसरे शब्दों में, हर बार जब आप एक नया फ़ंक्शन, सबरूटीन, या वेरिएबल घोषित करते हैं जो किसी मौजूदा पहचानकर्ता नाम का उपयोग करता है, तो उस पहचानकर्ता के अन्य सभी उदाहरणों में उनके मामले को मिलान में बदल दिया जाता है।
नीचे दिए गए इन उदाहरणों में से प्रत्येक में, केवल एक चीज जो मैं बदल रहा हूं वह है सूचीबद्ध पहला मॉड्यूल। VBA IDE पहले से परिभाषित मॉड्यूल में अन्य सभी परिवर्तनों के लिए जिम्मेदार है।
चरण 1:किसी फ़ंक्शन को परिभाषित करें
'--== Module1 ==--
Public Function ReloadDBData() As Boolean
End Function
चरण 2:समान नाम वाले उप को परिभाषित करें
नोट:यह तब तक पूरी तरह से मान्य है जब तक कि प्रक्रियाएं अलग-अलग मॉड्यूल में हों। उसने कहा, सिर्फ इसलिए कि आप कुछ *कर सकते हैं, इसका मतलब यह नहीं है कि आपको *चाहिए*। और यदि संभव हो तो आपको *इस स्थिति से* बचना चाहिए।
'--== Module2 ==--
Public Sub ReloadDbData()
End Sub
'--== Module1 ==--
Public Function ReloadDbData() As Boolean
End Sub
चरण 3:समान नाम वाले प्रकार को परिभाषित करें
नोट:फिर से, कृपया एक सब, फ़ंक्शन को परिभाषित न करें, और एक ही प्रोजेक्ट में एक ही नाम से सभी टाइप करें।
'--== Module3 ==--
Private Type ReLoadDBData
Dummy As Variant
End Type
'--== Module2 ==--
Public Sub ReLoadDBData()
End Sub
'--== Module1 ==--
Public Function ReLoadDBData() As Boolean
End Sub
चरण 4:एक ही नाम के साथ एक एनम को परिभाषित करें
नोट:कृपया, कृपया, कृपया, सभी पवित्र चीजों के प्यार के लिए...
'--== Module4 ==--
Public Enum ReloadDbDATA
Dummy
End Enum
'--== Module3 ==--
Private Type ReloadDbDATA
Dummy As Variant
End Type
'--== Module2 ==--
Public Sub ReloadDbDATA()
End Sub
'--== Module1 ==--
Public Function ReloadDbDATA() As Boolean
End Sub
चरण 5:समान नाम वाले चर को परिभाषित करें
नोट:हम वास्तव में अब भी यही कर रहे हैं?
'--== Module5 ==--
Public reloaddbdata As Boolean
'--== Module4 ==--
Public Enum reloaddbdata
Dummy
End Enum
'--== Module3 ==--
Private Type reloaddbdata
Dummy As Variant
End Type
'--== Module2 ==--
Public Sub reloaddbdata()
End Sub
'--== Module1 ==--
Public Function reloaddbdata() As Boolean
End Sub
चरण 6:समान नाम से स्थिरांक परिभाषित करें
नोट:ओह, चलो। गंभीरता से?
'--== Module6 ==--
Private Const RELOADDBDATA As Boolean = True
'--== Module5 ==--
Public RELOADDBDATA As Boolean
'--== Module4 ==--
Public Enum RELOADDBDATA
Dummy
End Enum
'--== Module3 ==--
Private Type RELOADDBDATA
Dummy As Variant
End Type
'--== Module2 ==--
Public Sub RELOADDBDATA()
End Sub
'--== Module1 ==--
Public Function RELOADDBDATA() As Boolean
End Sub
चरण 7:समान नाम वाली एक वर्ग संपत्ति परिभाषित करें
नोट:यह मूर्खतापूर्ण हो रहा है।
'--== Class1 ==--
Private Property Get reloadDBData() As Boolean
End Property
'--== Module6 ==--
Private Const reloadDBData As Boolean = True
'--== Module5 ==--
Public reloadDBData As Boolean
'--== Module4 ==--
Public Enum reloadDBData
Dummy
End Enum
'--== Module3 ==--
Private Type reloadDBData
Dummy As Variant
End Type
'--== Module2 ==--
Public Sub reloadDBData()
End Sub
'--== Module1 ==--
Public Function reloadDBData() As Boolean
End Sub
Enum Items?!?!
इस तीसरे बिंदु के लिए, एनम प्रकार . के बीच अंतर करना महत्वपूर्ण है और एक एनम आइटम .
Enum EnumTypeName ' <-- Enum type
EnumItemAlice ' <-- Enum item
EnumItemBob ' <-- Enum item
End Enum
हमने पहले ही ऊपर दिखाया है कि Enum प्रकारों को अन्य प्रकार की घोषणाओं के समान माना जाता है, जैसे कि उप, कार्य, स्थिरांक और चर। जब भी उस नाम वाले पहचानकर्ता के लिए घोषणा पंक्ति बदली जाती है, तो उसी नाम वाले प्रोजेक्ट में प्रत्येक अन्य पहचानकर्ता का नवीनतम परिवर्तन से मिलान करने के लिए उसका आवरण अद्यतन होता है।
Enum आइटम इस मायने में विशेष हैं कि वे एकमात्र प्रकार के पहचानकर्ता हैं जिनके आवरण को कोड की कोई भी पंक्ति . जब भी बदला जा सकता है जिसमें एनम आइटम का नाम बदल गया है।
चरण 1. Enum को परिभाषित और पॉप्युलेट करें
'--== Module7 ==--
Public Enum EnumTypeName
EnumItemAlice
EnumItemBob
End Enum
चरण 2. कोड में Enum आइटम देखें
'--== Module8 ==--
Sub TestEnum()
Debug.Print EnumItemALICE, EnumItemBOB
End Sub
परिणाम:कोड की नियमित लाइन से मेल खाने के लिए Enum प्रकार घोषणा परिवर्तन
'--== Module7 ==--
Public Enum EnumTypeName
EnumItemALICE
EnumItemBOB
End Enum
भाग 2:हम यहां कैसे पहुंचे?
मैंने आंतरिक वीबीए विकास टीम में किसी से कभी बात नहीं की है। मैंने कभी भी कोई आधिकारिक दस्तावेज नहीं देखा है कि वीबीए आईडीई जिस तरह से काम करता है वह क्यों करता है। तो, मैं जो लिखने जा रहा हूं वह शुद्ध अनुमान है, लेकिन मुझे लगता है कि यह कुछ समझ में आता है।
एक लंबे समय के लिए, मैंने सोचा कि दुनिया में वीबीए आईडीई का ऐसा व्यवहार क्यों होगा। आखिरकार, यह स्पष्ट रूप से जानबूझकर है। आईडीई के लिए सबसे आसान काम होगा...कुछ नहीं। यदि उपयोगकर्ता सभी कैप्स में एक चर घोषित करता है, तो इसे सभी कैप्स में खड़े होने दें। यदि उपयोगकर्ता कुछ पंक्तियों के बाद उस चर को लोअर केस में संदर्भित करता है, तो उस संदर्भ को लोअर केस में और मूल घोषणा को सभी कैप्स में छोड़ दें।
यह वीबीए भाषा का पूरी तरह से स्वीकार्य कार्यान्वयन होगा। आखिरकार, भाषा ही केस असंवेदनशील है। तो, पहचानकर्ता आवरण को स्वचालित रूप से बदलने के लिए सभी परेशानी क्यों करें?
विडंबना यह है कि मेरा मानना है कि प्रेरणा भ्रम से बचने के लिए थी। (स्विंग और मिस, अगर आप मुझसे पूछें।) मैं इस स्पष्टीकरण का उपहास करता हूं, लेकिन यह कुछ समझ में आता है।
केस-सेंसिटिव भाषाओं के साथ कंट्रास्ट
सबसे पहले, केस संवेदी भाषा से आने वाले प्रोग्रामर्स के बारे में बात करते हैं। केस-संवेदी भाषाओं में एक आम परंपरा है, जैसे कि C#, बड़े अक्षरों वाली क्लास ऑब्जेक्ट्स को नाम देना और उन ऑब्जेक्ट्स के इंस्टेंस को क्लास के समान नाम देना है, लेकिन एक प्रमुख लोअर-केस अक्षर के साथ।
वह सम्मेलन वीबीए में काम नहीं करेगा, क्योंकि दो पहचानकर्ता जो केवल आवरण में भिन्न होते हैं उन्हें समकक्ष माना जाता है। वास्तव में, Office VBA IDE आपको एक साथ एक प्रकार के आवरण के साथ एक फ़ंक्शन और एक अलग प्रकार के आवरण के साथ एक स्थानीय चर घोषित नहीं करने देगा (हमने इसे पूरी तरह से ऊपर कवर किया है)। यह डेवलपर को यह मानने से रोकता है कि एक ही अक्षर लेकिन अलग-अलग आवरण वाले दो पहचानकर्ताओं के बीच अर्थ संबंधी अंतर है।
गलत कोड को गलत दिखाना
मेरे दिमाग में संभावित व्याख्या यह है कि समान पहचानकर्ताओं को समान दिखने के लिए यह "फीचर" मौजूद है। इसके बारे में सोचो; इस सुविधा के बिना, टाइपो के लिए रनटाइम त्रुटियों में बदलना आसान होगा। मेरा विश्वास मत करो? इस पर विचार करें:
Private mAccountName As String
Private Const ACCOUNT_NAME As String = "New User"
Private Sub Class_Initialize()
mAccountName = ACCOUNT_NAME
End Sub
Public Property Get MyAccountName() As String
MAccountName = Account_Name
End Property
Public Property Let MyAccountName(AccountName As String)
mAccountName = Account_Name
End Property
यदि आप उपरोक्त कोड को जल्दी से देखते हैं, तो यह बहुत सीधा दिखता है। यह एक .MyAccountName . वाला वर्ग है संपत्ति। जब ऑब्जेक्ट बनाया जाता है तो संपत्ति के लिए सदस्य चर को स्थिर मान के लिए प्रारंभ किया जाता है। कोड में खाता नाम सेट करते समय, सदस्य चर फिर से अपडेट किया जाता है। संपत्ति मूल्य प्राप्त करते समय, कोड केवल सदस्य चर की सामग्री लौटाता है।
कम से कम, यही तो करना है। अगर मैं उपरोक्त कोड को कॉपी करता हूं और इसे वीबीए आईडीई विंडो में पेस्ट करता हूं, तो पहचानकर्ताओं का आवरण सुसंगत हो जाता है और रनटाइम बग अचानक खुद को दिखाते हैं:
Private mAccountName As String
Private Const ACCOUNT_NAME As String = "New User"
Private Sub Class_Initialize()
mAccountName = ACCOUNT_NAME ' <- This is OK
End Sub
Public Property Get MyAccountName() As String
mAccountName = ACCOUNT_NAME ' <- This is probably not what we intended
End Property
Public Property Let MyAccountName(AccountName As String)
mAccountName = ACCOUNT_NAME ' <- This is definitely not what we meant
End Property
कार्यान्वयन:क्या यह वास्तव में सबसे अच्छा तरीका है?
उम्म, नहीं। मुझे गलत मत समझो। मुझे वास्तव में स्थिरता बनाए रखने के लिए पहचानकर्ताओं के पूंजीकरण को स्वचालित रूप से बदलने का विचार वास्तव में पसंद है। मेरी एकमात्र वास्तविक शिकायत यह है कि पूरे प्रोजेक्ट में उस नाम के साथ प्रत्येक पहचानकर्ता में परिवर्तन किया जाता है। केवल उन पहचानकर्ताओं के पूंजीकरण को बदलना बेहतर होगा जो समान "चीज़" को संदर्भित करते हैं (चाहे वह "चीज़" एक फ़ंक्शन, उप, संपत्ति, चर, आदि हो)।
तो यह इस तरह से काम क्यों नहीं करता? मुझे उम्मीद है कि वीबीए आईडीई डेवलपर्स मेरे दृष्टिकोण से सहमत होंगे कि इसे कैसे काम करना चाहिए। लेकिन, एक बहुत अच्छा कारण है आईडीई इस तरह से काम क्यों नहीं करता है। एक शब्द में, प्रदर्शन।
दुर्भाग्य से, यह पता लगाने का केवल एक विश्वसनीय तरीका है कि समान नाम वाले कौन से पहचानकर्ता वास्तव में एक ही चीज़ को संदर्भित करते हैं:कोड की प्रत्येक पंक्ति को पार्स करें। वह sloooowwwww है। यह मेरी ओर से एक साधारण परिकल्पना से कहीं अधिक है। रबरडक वीबीए परियोजना वास्तव में ऐसा ही करती है; यह परियोजना में कोड की प्रत्येक पंक्ति को पार्स करता है ताकि यह स्वचालित कोड विश्लेषण और अन्य अच्छी चीजों का एक समूह कर सके।
परियोजना बेशक भारी है। यह शायद एक्सेल प्रोजेक्ट्स के लिए बहुत अच्छा काम करता है। दुर्भाग्य से, मैं अपने किसी भी एक्सेस प्रोजेक्ट में इसका उपयोग करने के लिए पर्याप्त धैर्यवान नहीं रहा हूं। रबरडक वीबीए एक तकनीकी रूप से प्रभावशाली परियोजना है, लेकिन यह एक सतर्क कहानी भी है। पहचानकर्ताओं के लिए पूंजीकरण बदलते समय गुंजाइश का सम्मान करना अच्छा होगा, लेकिन VBA IDE के वर्तमान तेजतर्रार प्रदर्शन की कीमत पर नहीं।
अंतिम विचार
मैं इस सुविधा के लिए प्रेरणा को समझता हूं। मुझे लगता है कि मैं यह भी समझता हूं कि इसे जिस तरह से लागू किया गया है उसे क्यों लागू किया गया है। लेकिन यह मेरे लिए वीबीए का एकमात्र सबसे पागल कर देने वाला करतब है।
अगर मैं ऑफिस वीबीए डेवलपमेंट टीम को एक भी सिफारिश कर सकता हूं, तो स्वचालित केस परिवर्तनों को अक्षम करने के लिए आईडीई में एक सेटिंग पेश करना होगा। वर्तमान व्यवहार डिफ़ॉल्ट रूप से सक्षम बना रह सकता है। लेकिन, पावर उपयोगकर्ताओं के लिए जो संस्करण नियंत्रण प्रणालियों के साथ एकीकृत करने का प्रयास कर रहे हैं, संशोधन इतिहास को दूषित करने से "कोड परिवर्तन" को रोकने के लिए व्यवहार को पूरी तरह से अक्षम किया जा सकता है।