2011-12-08 11 views
6

मेरा सी # कोड अनुकूलित करने की कोशिश कर रहा है, मैंने पाया कि यदि मेरे पास संरचना तत्वों की एक सूची है, तो प्रत्येक प्रविष्टि एक पूर्ण प्रतिलिपि होगी - कुछ जिसे मैं टालना चाहता हूं।सी # सूचियों में संरचनाओं के संदर्भ को जोड़ना

सी ++ में मैं बस पॉइंटर्स की एक सूची रखूंगा और इसलिए मैं सोच रहा था कि क्या मैं सी # का उपयोग कर ऐसा कर सकता हूं, शायद सूची के संदर्भों की सूची के रूप में सूची के द्वारा। दुर्भाग्य से संरचना को कक्षा में नहीं बदला जा सकता क्योंकि यह एक्सएनए लाइब्रेरी (वेक्टर 3, मैट्रिक्स, आदि ...) किसी भी मामले में - प्रारूप और उपयोग कैसा दिखता है यदि यह संभव है?

धन्यवाद।

+1

एक वर्ग है कि संरचना संग्रहीत करता है बनाएँ। समयपूर्व अनुकूलन के साथ बहुत सावधान रहें, पॉइंटर्स के सरणी में लुसी कैश इलाके है। –

+2

संरचना से संदर्भ कितना बड़ा है? संरचनाओं को छोटा माना जाना चाहिए; यदि वे किसी संदर्भ से इतने बड़े हैं कि प्रतिलिपि समय आपकी बाधा है तो हो सकता है कि आप पहले स्थान पर किसी संरचना का उपयोग नहीं करना चाहें। क्या आप अधिक जानकारी दे सकते हैं? –

+0

खैर, संदर्भ केवल 4bytes लगता है, इसलिए कुछ भी एक नाव या पूर्णांक से भी बड़ा पहले से ही उस से भी बड़ा है। यदि आप गणित करते हैं, भले ही मैं बहुत मामूली हूं और केवल एक साधारण वेक्टर 4 स्टोर करता हूं, अनुपात पहले से ही 4: 1 है, और यह एक बड़ी संरचना के करीब कुछ भी नहीं है। प्रोफाइलिंग प्रोफाइल के बारे में पूछने के लिए – Adi

उत्तर

5

नहीं, मूल रूप से। विकल्प:

  • उपयोग एक वर्ग (आप पहले से ही कहा है आप कर सकते हैं नहीं)
  • बॉक्स यह
  • बारे में एक वर्ग है कि यह लपेटता (अनिवार्य रूप से, मैनुअल मुक्केबाजी)
  • एक सरणी का उपयोग करें, और इसे तक पहुंचें केवल सीधे इंडेक्स द्वारा सरणी में (एक चर में कॉपी किए बिना); यह तब सरणी में आइटम (सीधे कोई प्रतिलिपि नहीं)

अंतिम के उदाहरण के रूप में;

if(arr[idx].X == 20) SomeMethod(ref arr[idx]); 

दोनों .X, और SomeMethod के भीतर किसी भी उपयोग, सीधे सरणी में मान तक पहुंच रहे हैं, प्रतिलिपि नहीं। यह केवल वैक्टर (सरणी) के साथ संभव है, सूची नहीं।

एक कारण structs के लिए रेफरी की एक सूची संभव नहीं है: यह आपको सूची में, ढेर पर एक चर के पते को स्टोर करने की अनुमति देगा; सरणी आमतौर पर स्टैक पर परिवर्तनीय रूप से बाहर निकलती है, इसलिए यह लज्जास्पद असुरक्षित होगा

+0

मैं अनजाने ढेर सी –

+1

में आप सी कि तुम सच में ऐसा नहीं करना चाहिए में चीजों के एक महान सौदा कर सकते हैं पर संरचनाओं के लिए संदर्भ भंडारण के द्वारा मज़ा कीड़े आप बना सकते हैं में से कुछ याद है। –

+0

@ रामहॉउंड, विशेष रूप से जब एक हैकर आपको पता चलता है कि आपको क्या नहीं करना चाहिए था और एप्लिकेशन को अपहृत करने के लिए अपने दूषित ढेर में डेटा इंजेक्ट करता है। –

5

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


नीचे संदर्भ प्रकार में लिपटे मान प्रकार का एक उदाहरण है। ध्यान दें कि यह केवल तभी काम करता है जब किसी विशेष मान तक पहुंच रैपिंग संदर्भ प्रकार के माध्यम से हो। यह इन्सुलेशन के मानक नियमों (सार्वजनिक क्षेत्र की वजह से) का उल्लंघन करता है, लेकिन यह एक विशेष मामला है।

public sealed class Reference<T> 
    where T: struct 
{ 
    public T Value; 

    public Reference(T value) 
    { 
     Value = value; 
    } 
} 

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

+0

+1। – Scott

0

यदि structs सरल प्रकार से बने होते हैं तो आप पॉइंटर्स की एक सरणी बना सकते हैं। सी # में हाँ पॉइंटर्स आपके जैसे मामलों के लिए बहुत उपयोगी हैं। यद्यपि सीमाएं हैं। मूल संरचनाओं को Array में संग्रहीत किया जाना चाहिए और List<> नहीं। unsafe निर्माण ध्वज के साथ संकलित नीचे दिए गए उदाहरण को देखें।

[StructLayout(LayoutKind.Sequential)] 
public struct Vec3 
{ 
    public double X, Y, Z; 
    public double Mag { get { return Math.Sqrt(X * X + Y * Y + Z * Z); } } 
} 

public unsafe class Vec3ArrayProxy 
{ 
    Vec3*[] ptr = null; //internal array of pointers 

    public Vec3ArrayProxy(Vec3[] array) 
    { 
     ptr = new Vec3*[array.Length]; //allocate array 
     fixed (Vec3* src = array) //src holds pointer from source array 
     { 
      for (int i = 0; i < array.Length; i++) 
      { 
       ptr[i] = &src[i]; //take address of i-th element 
      }     
     } 
    } 

    public Vec3ArrayProxy(Vec3ArrayProxy other) 
    { 
     //just use all the existing pointers 
     ptr = (Vec3*[])other.ptr.Clone(); 
     //or I could say: 
     //ptr = other.ptr; 
    } 
    // Access values with index 
    public Vec3 this[int index] 
    { 
     get { return *ptr[index]; } 
     set { *ptr[index] = value; } 
    } 
    public int Count { get { return ptr.Length; } } 
    // Access the array of pointers 
    public Vec3*[] PtrArray { get { return ptr; } } 
    // Copy the values of original array into new array 
    public Vec3[] ToArrayCopy() 
    { 
     Vec3[] res = new Vec3[ptr.Length]; 
     for (int i = 0; i < res.Length; i++) 
     { 
      res[i] = *ptr[i]; 
     } 
     return res; 
    } 

} 


unsafe class Program 
{ 
    static void Main(string[] args) 
    { 
     const int N = 10; //size of array 

     // Allocate array in memory 
     Vec3[] array = new Vec3[N]; 

     // Assign values into array 
     for (int i = 0; i < N; i++) 
     { 
      array[i] = new Vec3() { X = i, Y = 0, Z = 0 }; 
     } 

     //Build proxy to array (with pointers) 
     Vec3Array A = new Vec3Array(array); 
     // Reference the same pointers as A 
     Vec3Array B = new Vec3Array(A); 

     // Change the original array 
     array[4].X = -4; 

     // Or change via a copy 
     A.PtrArray[5]->Y = -5; 

     // Or assign a new value 
     B[0] = B[9];    

     // Show contents of array via proxy A 
     Console.WriteLine("{0,-6}|{1,6}|{2,6}|{3,6}|{4,6}", 
      "i", "X", "Y", "Z", "Mag"); 
     for (int i = 0; i < N; i++) 
     { 
      Console.WriteLine("{0,6}|{1,6:F2}|{2,6:F2}|{3,6:F2}|{4,6:F3}", 
       i + 1, A[i].X, A[i].Y, A[i].Z, A[i].Mag); 
     } 

    } 
} 

लंबे कोड के लिए खेद है, लेकिन मैं संरचना पॉइंटर्स की सभी सुविधाओं को दिखाना चाहता था।कारण List<> काम नहीं करेगा क्योंकि आप किसी सूची तत्व में पॉइंटर नहीं ले सकते हैं। क्या तुम सच में वास्तव में वास्तव में एक List<> का उपयोग करना चाहिए, तो निम्न कोड के साथ निजी क्षेत्र _items से सरणी निकालें:

static T[] ExtractArray(List<T> list) 
    { 
     //list.TrimExcess(); 
     var t = list.GetType(); 
     var items = t.GetField("_items", 
      BindingFlags.NonPublic | BindingFlags.Instance); 
     return items.GetValue(list) as T[]; 
    } 

क्रूड यह हो सकता है, यह काम करता है और एक बार आप एक बार List<> पर प्रतिबिंब बना दिया है, आप परिणाम को स्थिर क्षेत्र में कैश कर सकते हैं और प्रत्येक बार केवल items.GetValue(list) पर कॉल कर सकते हैं।

+0

इस प्रॉक्सी बनाने के दायरे से बाहर सरणी के स्थान पिन नहीं है और जब सरणी जीसी दौरान ले जाया जाता है कर सकते हैं इसलिए दुर्घटना भयानक तरीके है। –

+0

@DanBryant - दिलचस्प। मैं मूल सरणी एकत्र करने और दुर्घटना को डेमो करने के लिए जीसी को कैसे मजबूर कर सकता हूं? – ja72

+0

आप सरणी आवंटित करने से पहले छोटी वस्तुओं का एक गुच्छा आवंटित करने का प्रयास कर सकते हैं, फिर सरणी आवंटित करने के बाद उन्हें संदर्भित करें (इसलिए जीसी जानता है कि उन्हें थोड़ी देर के लिए रूट रहना था), फिर उन्हें संदर्भित करना बंद करें और जीसी के साथ जीसी को मजबूर करें। । मेरा मानना ​​है कि यह Gen0 की ढेर compaction को ट्रिगर करेगा और संभवतया सरणी को Gen1 तक भी बढ़ावा देगा; इनमें से किसी को स्मृति में सरणी को स्थानांतरित करना चाहिए। ध्यान दें कि यह दुर्घटनाग्रस्त होने की गारंटी नहीं है; यह सिर्फ चुपचाप स्मृति को दूषित कर सकता है। यदि आप नए जीसीडी स्पेस में एक बड़ी पर्याप्त सरणी आवंटित करते हैं, तो अपने मूल पॉइंटर्स तक पहुंचें, तो आप इसे देख सकते हैं। –

संबंधित मुद्दे