सभी को नमस्कार। मैं बाइट्स, किट्स और अन्य नंबरों के उदाहरणों की तुलना में अधिक दिलचस्प कुछ के लिए स्ट्रक्चरलैट का उपयोग करने का एक उदाहरण साझा करना चाहूंगा, जिसमें सब कुछ उम्मीद से थोड़ा अधिक होता है।
लाइटनिंग-फास्ट एनकैप्सुलेशन उल्लंघन पर शुरू करने से पहले, संक्षेप में यह याद रखने योग्य है कि स्ट्रक्चरलैयूट क्या है। कड़ाई से बोलते हुए, यह एक स्ट्रक्चरलआउटआउट भी है, अर्थात, एक विशेषता जो आपको C ++ में संघ के समान संरचना और कक्षाएं बनाने की अनुमति देती है। अधिक विस्तार से, यह विशेषता आपको स्मृति में वर्ग के सदस्यों के प्लेसमेंट का नियंत्रण लेने की अनुमति देती है। तदनुसार, इसे कक्षा से ऊपर रखा गया है। आमतौर पर, यदि किसी वर्ग के 2 क्षेत्र हैं, तो हम उम्मीद करते हैं कि वे क्रमबद्ध रूप से व्यवस्थित होंगे, अर्थात वे एक दूसरे से स्वतंत्र होंगे (ओवरलैप नहीं होंगे)। हालांकि, स्ट्रक्चरलैयूट यह निर्दिष्ट करना संभव बनाता है कि खेतों का स्थान पर्यावरण द्वारा नहीं, बल्कि उपयोगकर्ता द्वारा निर्धारित किया जाएगा। फ़ील्ड ऑफ़सेट्स को स्पष्ट रूप से इंगित करने के लिए, LayoutKind.Explicit पैरामीटर का उपयोग करें। यह इंगित करने के लिए कि कक्षा / संरचना की शुरुआत के सापेक्ष क्या ऑफसेट (बाद में वर्ग के रूप में संदर्भित किया जाता है) हम फ़ील्ड को रखना चाहते हैं, हमें फील्डऑफ़सेट विशेषता को इसके ऊपर रखना चाहिए, जो कि बाइट्स की संख्या के पैरामीटर के रूप में लेता है - कक्षा की शुरुआत से इंडेंट। एक नकारात्मक मान पारित नहीं किया जा सकता है, इसलिए एक पॉइंट टेबल या सिंक्रोनाइज़ेशन ब्लॉक के इंडेक्स को खराब करने के बारे में भी मत सोचो, यह थोड़ा अधिक जटिल होगा।
कोड लिखना शुरू करते हैं। शुरू करने के लिए, मैं एक सरल उदाहरण के साथ शुरुआत करने का सुझाव देता हूं। निम्नलिखित फॉर्म की एक कक्षा बनाएँ:
public class CustomClass { public override string ToString() { return "CUSTOM"; } public virtual object Field { get; } = new object(); }
अगला, हम उपरोक्त तंत्र को स्पष्ट रूप से फ़ील्ड ऑफ़सेट सेट करने के लिए उपयोग करते हैं।
[StructLayout(LayoutKind.Explicit)] public class CustomStructWithLayout { [FieldOffset(0)] public string Str; [FieldOffset(0)] public CustomClass SomeInstance; }
अभी के लिए, मैं स्पष्टीकरणों को स्थगित कर दूंगा और लिखित कक्षा का उपयोग इस प्रकार करूंगा:
class Program { static void Main(string[] args) { CustomStructWithLayout instance = new CustomStructWithLayout(); instance.SomeInstance = new CustomClass(); instance.Str = "4564"; Console.WriteLine(instance.SomeInstance.GetType()); //System.String Console.WriteLine(instance.SomeInstance.ToString()); //4564 Console.Read(); } }
कुल। गेटटाइप () विधि के लिए कॉल एक स्ट्रिंग लौटाता है, ToString () विधि शरारती है और हमें स्ट्रिंग "455" देती है।
मस्तिष्क के लिए निर्वहन: जब वर्चुअल प्रॉपर्टी CustomClass कहा जाता है तो क्या प्रदर्शित किया जाएगा?
जैसा कि आप अनुमान लगा सकते हैं, हमने CustomStructWithLayout को इनिशियलाइज़ किया है, दोनों लिंक अशक्त हैं, फिर हम अपने प्रकार के एक क्षेत्र को इनिशियलाइज़ करते हैं, और उसके बाद हम Str फ़ील्ड में एक स्ट्रिंग असाइन करते हैं। नतीजतन, कुछ नहीं की तुलना में CustomClass से थोड़ा अधिक रहता है। इसके शीर्ष पर इसकी सभी आंतरिक संरचना के साथ एक पंक्ति थी, जिसमें विधियों की एक तालिका और एक सिंक्रनाइज़ेशन ब्लॉक का एक सूचकांक शामिल था। लेकिन संकलक देखता है कि क्षेत्र अभी भी हमारी कक्षा के प्रकार का है।
प्रमाण के लिए, मैं WinDbg से एक छोटी क्लिपिंग दूंगा:

यहां आप कुछ असामान्य चीजें देख सकते हैं। पहला यह है कि ऑब्जेक्ट में विधि तालिकाओं पर पता फ़ील्ड फ़ील्ड के लिए अलग-अलग फ़ील्ड हैं, जैसा कि अपेक्षित है, लेकिन फ़ील्ड मान का पता एक है। दूसरा - आप देख सकते हैं कि दोनों फ़ील्ड्स ऑफ़सेट 4 पर स्थित हैं। मुझे लगता है कि अधिकांश लोग समझ जाएंगे, लेकिन सिर्फ मामले में, मैं समझाता हूं, सीधे ऑब्जेक्ट के पते पर विधियों की तालिका के लिए एक लिंक है। फ़ील्ड्स 4 बाइट्स (z 32 बिट्स के लिए) की ऑफसेट के साथ शुरू होती हैं, और सिंक्रनाइज़ेशन ब्लॉक का सूचकांक -4 की ऑफसेट के साथ स्थित है।
अब जब आपको पता चल गया है कि आप क्या कर रहे हैं, तो आप कुछ ऐसा करने के लिए ऑफ़सेट का उपयोग करने का प्रयास कर सकते हैं जिसे आपको कॉल नहीं करना चाहिए।
ऐसा करने के लिए, मैंने अपनी एक कक्षा में स्ट्रिंग क्लास की संरचना को दोहराया। सच है, मैंने केवल शुरुआत को दोहराया, चूंकि स्ट्रिंग वर्ग बहुत ही चमकदार है।
public class CustomClassLikeString { public const int FakeAlignConst = 3; public const int FakeCharPtrAlignConst = 3; public static readonly object FakeStringEmpty; public char FakeFirstChar; public int FakeLength = 3; public const int FakeTrimBoth = 3; public const int FakeTrimHead = 3; public const int FakeTrimTail = 3; public CustomClassLikeString(){} public CustomClassLikeString(int a){} public CustomClassLikeString(byte a){} public CustomClassLikeString(short a){} public CustomClassLikeString(string a){} public CustomClassLikeString(uint a){} public CustomClassLikeString(ushort a){} public CustomClassLikeString(long a){ } public void Stub1(){} public virtual int CompareTo(object value) { return 800; } public virtual int CompareTo(string value) { return 801; } }
खैर, लेआउट के साथ संरचना थोड़ी बदल जाती है
[StructLayout(LayoutKind.Explicit)] public class CustomStructWithLayout { [FieldOffset(0)] public string Str; [FieldOffset(0)] public CustomClassLikeString SomeInstance; }
इसके अलावा, जब FakeLength या ComparTo () विधि को कॉल करते हैं, तो ऑब्जेक्ट के पते के सापेक्ष इन वर्ग के सदस्यों के समान ऑफसेट के कारण, इसी स्ट्रिंग विधि (इस मामले में) को बुलाया जाएगा। पहली निजी पद्धति को प्राप्त करने में मुझे काफी समय लग गया, जिसका मैं उपयोग कर सकता हूं, इसलिए मैं जनता के बीच बस गया। लेकिन मैदान निजी है, सब कुछ उचित है। वैसे, तरीकों को आभासी बनाया जाता है ताकि सभी प्रकार के अनुकूलन से बचा जा सके, जो काम के साथ हस्तक्षेप करता है (उदाहरण के लिए, एम्बेडिंग), और इसलिए भी कि विधि तालिका में एक ऑफसेट पर कहा जाता है।
तो, प्रदर्शन। यह बात दीगर है कि चुनौती देने के लिए जो आवश्यक नहीं है उसका सीधा प्रतियोगी और एनकैप्सुलेशन के उल्लंघन में प्रतिबिंब है। मुझे लगता है कि यह पहले से ही स्पष्ट है कि हम इस चीज़ से तेज़ हैं, हम सभी मेटाडेटा का विश्लेषण नहीं करते हैं। सटीक मान:
विधि | काम | मीन | त्रुटि | StdDev | मंझला |
---|
StructLayoutField | clr | 0.0597 एन.एस. | 0.0344 एन.एस. | 0.0396 एन.एस. | 0.0498 एन.एस. |
ReflectionField | clr | 197.1257 एन.एस. | 1.9148 एन.एस. | 1.7911 एन.एस. | 197.4787 एन.एस. |
StructLayoutMethod | clr | 3.5195 एन.एस. | 0.0382 एन.एस. | 0.0319 एन.एस. | 3.5285 एन.एस. |
ReflectionMethod | clr | 743.9793 एन.एस. | 13.7378 एन.एस. | 12.8504 एन.एस. | 743.8471 एन.एस. |
यहां कोड का एक लंबा टुकड़ा है कि मैंने प्रदर्शन को कैसे मापा (यदि किसी को इसकी आवश्यकता है):
कोड [ClrJob] [RPlotExporter, RankColumn] [InProcessAttribute] public class Benchmarking { private CustomStructWithLayout instance; private string str; [GlobalSetup] public void Setup() { instance = new CustomStructWithLayout(); instance.SomeInstance = new CustomClassLikeString(); instance.Str = "4564"; str = "4564"; } [Benchmark] public int StructLayoutField() { return instance.SomeInstance.FakeLength; } [Benchmark] public int ReflectionField() { return (int)typeof(string).GetField("m_stringLength", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(str); } [Benchmark] public int StructLayoutMethod() { return instance.SomeInstance.CompareTo("4564"); } [Benchmark] public int ReflectionMethod() { return (int)typeof(string).GetMethod("CompareTo", new[] { typeof(string) }).Invoke(str, new[] { "4564" }); } } class Program { static void Main(string[] args) { var summary = BenchmarkRunner.Run<Benchmarking>(); } }