क्या मैं परावर्तन का उपयोग करके C # में निजी रीडोनॉली फ़ील्ड बदल सकता हूँ?


115

मैं सोच रहा हूं, क्योंकि प्रतिबिंब का उपयोग करके बहुत सारी चीजें की जा सकती हैं, क्या मैं निर्माण के पूरा होने के बाद एक निजी क्षेत्र को आसानी से बदल सकता हूं?
(नोट: सिर्फ जिज्ञासा)

public class Foo
{
 private readonly int bar;

 public Foo(int num)
 {
  bar = num;
 }

 public int GetBar()
 {
  return bar;
 }
}

Foo foo = new Foo(123);
Console.WriteLine(foo.GetBar()); // display 123
// reflection code here...
Console.WriteLine(foo.GetBar()); // display 456

जवाबों:


151

आप ऐसा कर सकते हैं:

typeof(Foo)
   .GetField("bar",BindingFlags.Instance|BindingFlags.NonPublic)
   .SetValue(foo,567);

2
आप बिल्कुल सही हैं, बिल्कुल। मैं क्षमाप्रार्थी हूं। और हाँ, मैंने कोशिश की, लेकिन मैंने एक बैकिंग फ़ील्ड का उपयोग न करते हुए, सीधे एक पठनीय संपत्ति स्थापित करने का प्रयास किया। मैंने जो प्रयास किया उसका कोई मतलब नहीं था। आपका समाधान पूरी तरह से ठीक काम करता है (फिर से, इस बार सही तरीके से परीक्षण किया गया)
ऋषि पोरपेर

हम moq के साथ यह कैसे कर सकते हैं?
l -'''''''--------- '' '' '' '' '' ''

डॉटनेट कोर 3.0 में यह अब संभव नहीं है। एक System.FieldAccessException को यह कहते हुए फेंक दिया जाता है: "फू 'इनिशियलाइज़ होने के बाद" स्थैतिक क्षेत्र को' बार 'सेट नहीं किया जा सकता है। "
डेविड परफोर्स

54

स्पष्ट बात यह कोशिश करना है:

using System;
using System.Reflection;

public class Test
{
    private readonly string foo = "Foo";

    public static void Main()
    {
        Test test = new Test();
        FieldInfo field = typeof(Test).GetField
            ("foo", BindingFlags.Instance | BindingFlags.NonPublic);
        field.SetValue(test, "Hello");
        Console.WriteLine(test.foo);
    }        
}

यह ठीक काम करता है। (जावा के अलग-अलग नियम हैं, दिलचस्प रूप से - आपको Fieldसुलभ होने के लिए स्पष्ट रूप से सेट करना होगा, और यह केवल उदाहरण के लिए वैसे भी काम करेगा।)


4
अहमद - लेकिन हम भाषा का प्रयोग नहीं कर रहे हैं यह करने के लिए है, इसलिए भाषा कल्पना एक वोट नहीं मिलता है ...
मार्क Gravell

4
युप - ऐसा बहुत कुछ है जो किया जा सकता है जो "टूटता है" जो भाषा चाहती है। आप उदाहरण के लिए, कई बार टाइप इनिशियलाइज़र चला सकते हैं।
जॉन स्कीट

28
मैं यह भी नोट करता हूं कि सिर्फ इसलिए कि आप आज कुछ कार्यान्वयन में हैं इसका मतलब यह नहीं है कि आप हर समय हर कार्यान्वयन पर कर सकते हैं। मुझे ऐसी किसी भी जगह के बारे में पता नहीं है, जहाँ हम दस्तावेज़ देते हैं कि प्रतिबिंब के माध्यम से आसानी से खेतों को बदलना चाहिए। जहां तक ​​मुझे पता है, सीएलआई का एक अनुरूप कार्यान्वयन, पठनीय क्षेत्रों को लागू करने के लिए पूरी तरह से स्वतंत्र है, जब वे निर्माण के बाद प्रतिबिंब के माध्यम से उत्परिवर्तित होने पर अपवाद छोड़ देते हैं।
एरिक लिपर्ट

3
हालांकि यह अच्छा नहीं है, क्योंकि ऐसे मामले हैं जब मुझे एक वर्ग का विस्तार करने की आवश्यकता है, जो मूल रूप से होने के लिए डिज़ाइन किया गया था। हमेशा सुनियोजित होने पर एनकैप्सुलेशन को ओवरराइड करने का एक तरीका होना चाहिए। एकमात्र विकल्प गैरी हैं और कभी-कभी फ्रेमवर्क के पुनर्लेखन के बिना संभव नहीं है।
ड्रिफ्टर

5
@drifter: उस बिंदु पर, आप खुद को दर्द की दुनिया के लिए खोल रहे हैं। आप वर्तमान कार्यान्वयन विवरणों पर भरोसा कर रहे हैं जो आसानी से भविष्य के संस्करण में बदल सकते हैं।
जॉन स्कीट

11

मैं इसके अन्य उत्तरों से सहमत हूं कि यह आम तौर पर और विशेष रूप से ई। लिप्टर्ट की टिप्पणी के साथ काम करता है कि यह प्रलेखित व्यवहार नहीं है और इसलिए भविष्य-प्रमाण कोड नहीं है।

हालाँकि, हमने एक और मुद्दा भी देखा। यदि आप अपने कोड को प्रतिबंधित अनुमतियों वाले वातावरण में चला रहे हैं तो आपको अपवाद मिल सकता है।

हमारे पास बस एक मामला है जहां हमारे कोड ने हमारी मशीनों पर ठीक काम किया है, लेकिन VerificationExceptionएक प्रतिबंधित वातावरण में कोड चलने पर हमें प्राप्त हुआ । अपराधी एक पठनीय क्षेत्र के सेटर के लिए एक प्रतिबिंब कॉल था। यह तब काम आया जब हमने उस क्षेत्र के पठनीय प्रतिबंध को हटा दिया।


2
यह जानने के लिए दिलचस्पी होगी कि कौन से वातावरण वेरिफिकेशन
एक्ससेप्शन

4

आपने पूछा कि आप इस तरह से एनकैप्सुलेशन को क्यों तोड़ना चाहेंगे।

मैं संस्थाओं को हाइड करने के लिए एक इकाई सहायक वर्ग का उपयोग करता हूं। यह एक नई खाली इकाई के सभी गुणों को प्राप्त करने के लिए प्रतिबिंब का उपयोग करता है, और परिणाम में कॉलम में संपत्ति / क्षेत्र के नाम से मेल खाता है, और इसे propertyinfo.setvalue () का उपयोग करके सेट करें।

मैं नहीं चाहता कि कोई और मूल्य बदल सके, लेकिन मैं हर इकाई के लिए कस्टम कोड हाइड्रेशन विधियों के लिए सभी प्रयास नहीं करना चाहता।

मेरे कई संग्रहित procs रिटर्न परिणाम हैं जो सीधे तालिकाओं या विचारों के अनुरूप नहीं हैं, इसलिए कोड जीन ORM मेरे लिए कुछ नहीं करता है।


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

3

असुरक्षित उपयोग करके ऐसा करने का एक और सरल तरीका (या आप DLLImport के माध्यम से सी विधि में फ़ील्ड को पास कर सकते हैं और इसे वहां सेट कर सकते हैं)।

using System;

namespace TestReadOnly
{
    class Program
    {
        private readonly int i;

        public Program()
        {
            i = 66;
        }

        private unsafe void ForceSet()
        {
            fixed (int* ptr = &i) *ptr = 123;
        }

        static void Main(string[] args)
        {
            var program = new Program();
            Console.WriteLine("Contructed Value: " + program.i);
            program.ForceSet();
            Console.WriteLine("Forced Value: " + program.i);
        }
    }
}

2

इसका उत्तर हां है, लेकिन इससे भी महत्वपूर्ण:

आपको क्यों चाहिए होगा? जानबूझकर ब्रेकिंग इनकैप्सुलेशन मुझे एक भयानक रूप से बुरा विचार लगता है।

एक पठनीय या निरंतर क्षेत्र को बदलने के लिए प्रतिबिंब का उपयोग करना मर्फी के कानून के साथ अनपेक्षित परिणामों के कानून के संयोजन की तरह है ।


1
उत्तर "बस जिज्ञासा" है, जैसा कि ऊपर उल्लेख किया गया है।
रॉन क्लेन

ऐसे समय होते हैं जब मैं खुद को सबसे अच्छा कोड लिखने के लिए सिर्फ इस ट्रिक को करने के लिए पाता हूं। बिंदु में मामला - सुरुचिपूर्णकोड .2008
04/

3
मैं भी इस ट्रिक का इस्तेमाल यूनिट टेस्टिंग प्रोजेक्ट्स में उन डिफॉल्ट वैल्यूज को ओवरराइड करने के लिए करता हूं, जिन्हें किसी भी बिजनेस कोड में नहीं बदलना चाहिए ...
Koen

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

3
ऐसी परिस्थितियां हैं जहां यह समझ में आता है। NHibernate जैसे ओ / आर मैपर हाइड्रेशन के लिए हर समय करते हैं, क्योंकि यह एकमात्र तरीका है जिससे आप पहले स्थान पर लगातार संस्थाओं के लिए डेटा एनकैप्सुलेशन को लागू कर सकते हैं।
क्रिस

2

यह मत करो।

मैंने सिर्फ एक दिन एक असली बग को ठीक करने में बिताया, जहां वस्तुएं अपने स्वयं के घोषित प्रकार की नहीं हो सकती थीं।

पठनीय क्षेत्र को संशोधित करके एक बार काम किया। लेकिन अगर आपने इसे फिर से संशोधित करने की कोशिश की, तो आपको इस तरह की परिस्थितियां मिलेंगी:

SoundDef mySound = Reflection_Modified_Readonly_SoundDef_Field;
if( !(mySound is SoundDef) )
    Log("Welcome to impossible-land!"); //This would run

तो यह मत करो।

यह मोनो रनटाइम (यूनिटी गेम इंजन) पर था।


2
FYI करें - एकता इंजन का उपयोग इस तरह के रूप में गहरे C # भाषा-विशिष्ट सवालों के प्रभावी ढंग से जवाब देने के लिए नहीं किया जा सकता है क्योंकि एकता प्रदर्शन करती है यह C # का स्वयं का संकलन है जैसे कि .cs स्क्रिप्ट थे, एक अर्थ में। मैं यह नहीं कह रहा हूं कि आपकी बात मान्य नहीं है, लेकिन यह निश्चित रूप से एकता इंजन और सी # के लिए विशिष्ट है।
डेव जेलिसन

0

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

ए) प्राइवेटऑब्जेक्ट क्लास

बी) आपको अभी भी एक PrivateObject उदाहरण की आवश्यकता होगी, लेकिन आप विजुअल स्टूडियो के साथ "एक्सेसर" ऑब्जेक्ट उत्पन्न कर सकते हैं। कैसे करें: निजी एक्सेसरों का पुनर्जनन करें

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.