2012-01-11 12 views
32

मुझे पता है कि मैं संपत्तियों के लिए वर्बोज़ सिंटैक्स का उपयोग कर सकते हैं:क्या ऑटो-कार्यान्वित गुणों के पीछे बैकिंग फ़ील्ड तक पहुंच बनाना संभव है?

private string _postalCode; 

public string PostalCode 
{ 
    get { return _postalCode; } 
    set { _postalCode = value; } 
} 

या मैं ऑटो कार्यान्वित गुणों का उपयोग कर सकते हैं।

public string PostalCode { get; set; } 

क्या मैं किसी भी तरह से ऑटो-लागू संपत्ति के पीछे बैकिंग फ़ील्ड तक पहुंच सकता हूं? (इस उदाहरण में _डाक कोड होगा)।


संपादित: मेरा प्रश्न डिजाइन के बारे में नहीं है, बल्कि के बारे में, मान लें कि ऐसा करने के लिए सैद्धांतिक क्षमता।

+6

उत्सुक होना !! तुम उसे क्यों चाहते हो? – Azodious

+0

कार्यान्वयन के लिए प्रोग्रामिंग खतरनाक है। – Eranga

+0

आप इसे निजी प्रतिबिंब के साथ एक्सेस कर सकते हैं, लेकिन यह एक बुरा विचार है। – CodesInChaos

उत्तर

33

मैं तुम्हारे बारे में पता नहीं है, लेकिन मैं अन्य कंपनियों में परियोजनाओं में कोड लिखा है, और अब मैं चाहता हूँ यह जानने के लिए कि मैंने कुछ कैसे किया! इसलिए आमतौर पर उत्तर के लिए वेब खोज करना तेज़ होता है, और यह मुझे यहां लाया।

हालांकि, मेरे कारण अलग हैं। मैं इकाई परीक्षण कर रहा हूं, और परवाह नहीं करता कि शुद्धियों को क्या कहना है, लेकिन यूनिट परीक्षण के लिए सेटअप के हिस्से के रूप में, मैं किसी दिए गए ऑब्जेक्ट के लिए एक निश्चित स्थिति का आह्वान करने की कोशिश कर रहा हूं। लेकिन उस राज्य को आंतरिक रूप से नियंत्रित किया जाना चाहिए। मैं नहीं चाहता कि कुछ अन्य डेवलपर गलती से राज्य के साथ गड़बड़ कर रहे हों, जो सिस्टम पर बहुत अधिक प्रभाव डाल सकता है। तो यह निजी रूप से सेट किया जाना चाहिए! फिर भी आप इस तरह के कुछ परीक्षणों का आविष्कार किए बिना यूनिट का परीक्षण कैसे करते हैं (उम्मीद है) कभी नहीं होगा? ऐसे परिदृश्यों में, मेरा मानना ​​है कि यूनिट परीक्षण के साथ प्रतिबिंब का उपयोग करना उपयोगी है।

विकल्प उन चीज़ों का खुलासा करना है जिन्हें हम उजागर नहीं करना चाहते हैं, इसलिए हम उन्हें यूनिट का परीक्षण कर सकते हैं! हां, मैंने इसे वास्तविक जीवन वातावरण में देखा है, और इसके बारे में सोचने से मुझे अभी भी मेरा सिर हिलाता है।

तो, मुझे आशा है कि नीचे दिया गया कोड उपयोगी हो सकता है।

चिंताओं को अलग करने, वास्तव में, और पठनीयता में सहायता के लिए यहां दो विधियां हैं। प्रतिबिंब अधिकांश डेवलपर्स के लिए सिर-कताई सामग्री है, जो मेरे अनुभव में या तो इससे दूर भागते हैं, या प्लेग की तरह इससे बचते हैं!

private string _getBackingFieldName(string propertyName) 
{ 
    return string.Format("<{0}>k__BackingField", propertyName); 
} 

private FieldInfo _getBackingField(object obj, string propertyName) 
{ 
    return obj.GetType().GetField(_getBackingFieldName(propertyName), BindingFlags.Instance | BindingFlags.NonPublic); 
} 

मैं नहीं जानता कि क्या कोड सम्मेलनों आप के लिए काम करते हैं, लेकिन व्यक्तिगत रूप से, मैं सहायक तरीकों निजी हो सकता है और एक कम मामले पत्र के साथ शुरू करना चाहते। मुझे पढ़ने के दौरान पर्याप्त स्पष्ट नहीं लगता है, इसलिए मुझे पहले अंडरस्कोर भी पसंद है।

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

अधिक कठिन सहायक _getBackingField उन प्रतिबिंब प्रकारों में से एक लौटाता है, FieldInfo। मैंने यहां भी एक धारणा बनाई है, कि आपके पीछे आने वाला बैकिंग फ़ील्ड एक ऑब्जेक्ट से है जो एक उदाहरण है, स्थिर होने के विपरीत। यदि आप चाहें तो पारित होने के लिए आप तर्कों को तोड़ सकते हैं, लेकिन पानी निश्चित रूप से औसत डेवलपर के लिए गड़बड़ कर देगा जो कार्यक्षमता चाहते हैं लेकिन समझ में नहीं।

FieldInfo एस के बारे में आसान बात यह है कि वे FieldInfo से मेल खाने वाली वस्तुओं पर फ़ील्ड सेट कर सकते हैं। यह बेहतर एक उदाहरण के साथ समझाया है:

var field = _getBackingField(myObjectToChange, "State"); 
field.SetValue(myObjectToChange, ObjectState.Active); 

इस मामले में, क्षेत्र ObjectState नामक एक गणन प्रकार का है। निर्दोषों की सुरक्षा के उद्देश्य से नाम परिवर्तित कर दिए गए हैं! तो, दूसरी पंक्ति में, आप देख सकते हैं कि FieldInfo तक पहुंचकर पहले लौटाया गया था, मैं SetValue विधि पर कॉल कर सकता हूं, जो आपको लगता है कि पहले से ही आपके ऑब्जेक्ट से संबंधित होना चाहिए, लेकिन नहीं! यह प्रतिबिंब की प्रकृति है- FieldInfo उस क्षेत्र से अलग करता है जहां से यह आया था, इसलिए आपको यह कहना होगा कि किस उदाहरण के साथ काम करना है (myObjectToChange) और इस प्रकार, जिस मान को आप चाहते हैं, इस मामले में ObjectState.Active

तो एक लंबी कहानी कम करने के लिए, ऑब्जेक्ट उन्मुख प्रोग्रामिंग हमें निजी क्षेत्रों तक पहुंचने जैसी बदसूरत चीजें करने से रोकती है, और बदतर, कोड के डेवलपर का इरादा नहीं होने पर उन्हें बदलना। कौन सा अच्छा है! यही कारण है कि सी # इतना मूल्यवान है, और डेवलपर्स द्वारा पसंद किया जाता है।

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

2

नहीं, ऐसा नहीं है। यदि आप बैकिंग फ़ील्ड तक पहुंचना चाहते हैं, तो ऑटो गुणों का उपयोग न करें और अपना खुद का रोल न करें।

प्रलेखन से Auto-Implemented Properties के लिए:

जब आप एक संपत्ति के रूप में निम्नलिखित उदाहरण में दिखाया गया घोषित, संकलक एक निजी, अनाम समर्थन क्षेत्र है कि केवल संपत्ति के प्राप्त और सेट accessors के माध्यम से पहुँचा जा सकता है बनाता है।

+0

क्या आपके पास कोई आधिकारिक संदर्भ है? –

+0

मेरे उत्तर में प्रदान किया गया। – Krizz

+0

मेरा जवाब अपडेट किया गया। – KMan

3

this document से निम्नलिखित अंश देखें:

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

इस प्रकार, फ़ील्ड तक पहुंचने का कोई तरीका नहीं है। अपना पहला दृष्टिकोण उपयोग करें। स्वत: कार्यान्वित गुण विशेष रूप से उस मामले के लिए हैं जहां आपको बैकिंग फ़ील्ड तक पहुंचने की आवश्यकता नहीं है।

8

यह सीधे MSDN से आता है:

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

तो नहीं, आप नहीं कर सकते।

+3

निष्कर्ष 'तो नहीं, आप नहीं कर सकते' सच नहीं है। यह सुलभ नहीं होने के बारे में कुछ भी नहीं कहता है। निजी क्षेत्र उदाहरण के लिए प्रतिबिंब का उपयोग कर अभी भी उपयोग किया जा सकता है। – Didii

1

Auto-implemented properties बैकिंग फ़ील्ड के साथ मैन्युअल रूप से कार्यान्वित संपत्ति का "आलसी" संस्करण है। चूंकि वे किसी भी अतिरिक्त तर्क की अनुमति नहीं देते हैं, केवल एक चीज जो आप उनके साथ कर सकते हैं वह है "छुपा" निजी क्षेत्र को पढ़ना या लिखना।आप अभी भी private संशोधक एक्सेसर्स एक्सेसर्स में से किसी एक को एक्सेस कर सकते हैं (आमतौर पर, set उस मामले में निजी होगा), यदि आप अपने निजी तरीकों को यह विशेषाधिकार प्राप्त करना चाहते हैं।

यदि आप कुछ अन्य वर्ग की निजी क्षेत्रों, आप Reflection इस्तेमाल कर सकते हैं यह करने के लिए (के रूप में these examples में विस्तार से बताया) (बीसीएल से एक वर्ग की तरह) का उपयोग करना चाहता था, लेकिन यह एक बुरा हैक होगा जहाँ कोई भी गारंटी देगा कि ढांचे के स्रोत में एक पत्र परिवर्तन भविष्य में आपके कोड को तोड़ नहीं देगा।

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

उल्लेख नहीं है कि संकलक शायद हर कॉल को इनलाइन कर देगा, इसलिए no performance difference भी है।

FieldInfo[] myInfo = ClassWithPostalCode.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic); 

आप कर सकते हैं तो लूप के माध्यम से:

+0

वीबी में ऐसा करना आम बात है, जब आप उस वर्ग के निर्माता में बैकिंग फ़ील्ड मान सेट करना चाहते हैं जिस पर स्वत: कार्यान्वित संपत्ति संबंधित है। मैं सहमत हूं कि इसे कन्स्ट्रक्टर के बाहर सेट करने के लिए कोई वैध कारण नहीं है। मैं बहुत चौंक गया था (और इसलिए खोज जो मुझे यहां ले जाती है) यह पता लगाने के लिए कि आप इसे सी # में नहीं कर पाए। लेकिन अब मैं वास्तव में उलझन में हूं, क्योंकि आपने कहा था "केवल एक चीज जो आप उनके साथ कर सकते हैं वह है" छुपा "निजी क्षेत्र पढ़ना या लिखना। –

+0

@Yann: मैं वीबी का उपयोग नहीं करता, इसलिए मुझे यकीन नहीं है कि मैं आपको समझ गया हूं, लेकिन सी # में एक 'रीडोनली' संशोधक है जो आपको केवल निजी क्षेत्र को कन्स्ट्रक्टर में सेट करने की अनुमति देता है। यदि केवल फ़ील्ड्स पर लागू किया जा सकता है, गुण नहीं, तो यदि आप स्वत: कार्यान्वित गुणों का उपयोग करते हैं तो इसका उपयोग करने का कोई तरीका नहीं है। – Groo

5

कम से कम दृश्य स्टूडियो 2010 में, आप यदि आप स्पष्ट रूप से यह है कि आप गैर सार्वजनिक दृष्टांत फ़ील्ड चाहते प्रतिबिंब का उपयोग कर एक वर्ग में निजी क्षेत्रों की सूची प्राप्त कर सकते हैं FieldInfo सरणी। इस मामले में आप देखेंगे कि समर्थन फ़ील्ड का नाम शायद हो जाएगा

<PostalCode> k__BackingField

मैंने देखा है कि सब स्वत: गुण कोण में संपत्ति के नाम के पैटर्न का पालन करने लगते हैं "k__BackingField" के बाद ब्रैकेट्स, लेकिन ध्यान रखें कि यह आधिकारिक नहीं है और नेट के भविष्य के संस्करणों में बदल सकता है। मैं पूरी तरह से निश्चित नहीं हूं कि यह पिछले संस्करणों में अलग नहीं है, उस मामले के लिए।

एक बार जब आप फ़ील्ड नाम पता है, तुम अपने मूल्य इस तरह से प्राप्त कर सकते हैं:

object oValue = obj.GetType().InvokeMember(fi.Name 
    , BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance 
    , null, obj, null); 
3

अद्यतन: https://github.com/jbevain/mono.reflection बैकिंग-फील्ड रिज़ॉल्वर विधि के साथ आता है जो सी #, वीबी.नेट और एफ # द्वारा उत्पन्न ऑटो-प्रॉपर्टी के साथ काम करता है। NuGet पैकेज https://www.nuget.org/packages/Mono.Reflection/

मूल: मैं केवल सी # ऑटो-गुणों के लिए इस काफी लचीली विधि के साथ समाप्त हुआ। चूंकि अन्य उत्तरों स्पष्ट करते हैं, यह पोर्टेबल नहीं है और यदि काम नहीं करता है तो संकलक कार्यान्वयन <PropertyName>k__BackingField के अलावा बैकिंग फ़ील्ड नामकरण योजना का उपयोग करता है। जहां तक ​​मैंने देखा है, वर्तमान में सी # कंपाइलर्स के सभी कार्यान्वयन इस नामकरण योजना का उपयोग करते हैं। वीबी.नेट और एफ # कंपाइलर्स एक और नामकरण योजना का उपयोग करते हैं जो इस कोड के साथ काम नहीं करेगा।

private static FieldInfo GetBackingField(PropertyInfo pi) { 
    if (!pi.CanRead || !pi.GetGetMethod(nonPublic:true).IsDefined(typeof(CompilerGeneratedAttribute), inherit:true)) 
     return null; 
    var backingField = pi.DeclaringType.GetField($"<{pi.Name}>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic); 
    if (backingField == null) 
     return null; 
    if (!backingField.IsDefined(typeof(CompilerGeneratedAttribute), inherit:true)) 
     return null; 
    return backingField; 
} 
संबंधित मुद्दे