विशेषता प्रबंधन प्रणाली

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

परिचय


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

विशिष्ट उदाहरण


जोड़ने


मान लीजिए कि हमारे पास किसी बाहरी स्रोत में उपयोगकर्ता इंटरफ़ेस (UI, फॉर्म, फॉर्म) का कुछ विवरण है। प्रपत्र प्रपत्र का एक आधार वर्ग है, जो आपको विवरण को लोड करने और सभी आवश्यक नियंत्रण (विजेट, नियंत्रण, विजेट) बनाने की अनुमति देता है। प्रपत्र उपयोगकर्ता प्रपत्र श्रेणी से इनहेरिट करते हैं और विशेषताओं का उपयोग करके आवश्यक फ़ील्ड को चिह्नित करते हैं। जब फॉर्म क्लास की प्रारंभिक विधि को बुलाया जाता है, तो बनाए गए विजेट उत्तराधिकारी वर्ग के क्षेत्रों से बंधे होते हैं।
स्रोत कोड
विजेट वर्ग विवरण:
using System; public class Widget { } 

विशेषता वर्ग विवरण:
 using System; [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)] public class WidgetNameAttribute : Attribute { public WidgetNameAttribute(string name) { this.name = name; } public string Name { get { return name; } } private string name; } 

फार्म वर्ग का विवरण:
 using System; using System.Reflection; public class Form { public void Initialise() { FieldInfo[] fields = GetType().GetFields( BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo field in fields) { foreach (WidgetNameAttribute item in field.GetCustomAttributes( typeof(WidgetNameAttribute), false)) { Widget widget = FindWidget(item.Name); field.SetValue(this, widget); break; } } } public Widget FindWidget(string name) { return new Widget(); } } 

कस्टम प्रपत्र वर्ग विवरण:
 using System; public class TestForm1 : Form { [WidgetName("Test1")] public Widget TestWidget1; [WidgetName("Test2")] public Widget TestWidget2; } 

प्रपत्र डाउनलोड और आरंभ करें:
 using System; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { TestForm1 form1 = new TestForm1(); form1.Initialise(); } } } 


क्रमबद्धता


मान लीजिए कि हमारे पास xml फ़ाइल में डेटा का कुछ विवरण है। यह डेटा और उदाहरण के लिए, सेटिंग्स दोनों हो सकता है। एक बेस क्लास डेटा है, जो आपको मूल्यों को खोजने और उन्हें आवश्यक प्रकार में बदलने की अनुमति देता है। उपयोगकर्ता डेटा वर्ग से विरासत में मिला है और फ़ील्ड को विशेषताओं के साथ चिह्नित करता है, जहां XPath xml में विशिष्ट फ़ील्ड मान के लिए पथ को इंगित करता है। जब डेटा वर्ग की InitialiseByXml विधि को बुलाया जाता है, तो मूल्य खोजा जाता है और आवश्यक प्रकार में परिवर्तित किया जाता है। वर्तमान उदाहरण में, सादगी के लिए, कनवर्टर कक्षा में बनाया गया है और केवल कुछ प्रकारों का समर्थन करता है।
स्रोत कोड
विशेषता वर्ग विवरण:
 using System; [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)] public class DataValueAttribute : Attribute { public DataValueAttribute(string xPath) { this.xPath = xPath; } public string XPath { get { return xPath; } } private string xPath; } 

डेटा वर्ग विवरण:
 using System; using System.Xml; using System.Reflection; using System.Collections.Generic; public class DataObject { static DataObject() { parsers[typeof(int)] = delegate(string value) { return int.Parse(value); }; parsers[typeof(string)] = delegate(string value) { return value; }; } public void InitialiseByXml(XmlNode node) { FieldInfo[] fields = GetType().GetFields( BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo field in fields) { foreach (DataValueAttribute item in field.GetCustomAttributes( typeof(DataValueAttribute), false)) { XmlNode tergetNode = node.SelectSingleNode(item.XPath); object value = parsers[field.FieldType](tergetNode.InnerText); field.SetValue(this, value); break; } } } private delegate object ParseHandle(string value); private static Dictionary<Type, ParseHandle> parsers = new Dictionary<Type, ParseHandle>(); } 

कस्टम वर्ग विवरण:
 using System; public class TestDataObject1 : DataObject { [DataValue("Root/IntValue")] public int Value1; [DataValue("Root/StringValue")] public string Value2; } 

उपयोग उदाहरण:
 using System; using System.Xml; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { XmlDocument doc = new XmlDocument(); doc.LoadXml( "<Root>" + "<IntValue>42</IntValue>" + "<StringValue>Douglas Adams</StringValue>" + "</Root>"); TestDataObject1 test = new TestDataObject1(); test.InitialiseByXml(doc); } } } 


क्लोनिंग


मान लीजिए कि हमारे पास एक क्लोन करने योग्य ऑबजेक्ट है जो हमें खुद को और हमारे सभी वंशजों को क्लोन करने की अनुमति देता है। क्लोनिंग या तो साधारण या गहरी हो सकती है। उपयोगकर्ता CloneableObject से वारिस करता है और क्लोन किए जाने वाले फ़ील्ड को चिह्नित करता है, और यह भी इंगित करता है कि गहरे क्लोनिंग फ़ील्ड का उपयोग करना है या नहीं।
स्रोत कोड
विशेषता वर्ग विवरण:
 using System; [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)] public class CloneAttribute : Attribute { public bool Deep { get { return deep; } set { deep = value; } } private bool deep; } 

CloneableObject वर्ग का विवरण:
 using System; using System.Reflection; public class CloneableObject : ICloneable { public object Clone() { object clone = Activator.CreateInstance(GetType()); FieldInfo[] fields = GetType().GetFields( BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo field in fields) { foreach (CloneAttribute item in field.GetCustomAttributes( typeof(CloneAttribute), false)) { object value = field.GetValue(this); if (item.Deep) { if (field.FieldType.IsArray) { Array oldArray = (Array)value; Array newArray = (Array)oldArray.Clone(); for (int index = 0; index < oldArray.Length; index++) newArray.SetValue(((ICloneable)oldArray.GetValue(index)). Clone(), index); value = newArray; } else { value = ((ICloneable)value).Clone(); } } field.SetValue(clone, value); break; } } return clone; } } 

कस्टम वर्ग विवरण:
 using System; public class TestCloneableObject1 : CloneableObject { [Clone] public CloneableObject Value1; [Clone] public CloneableObject[] ValueArray2; [Clone(Deep = true)] public CloneableObject Value3; [Clone(Deep = true)] public CloneableObject[] ValueArray4; } 

उपयोग उदाहरण:
 using System; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { TestCloneableObject1 test = new TestCloneableObject1(); test.Value1 = new CloneableObject(); test.ValueArray2 = new CloneableObject[] { new CloneableObject() }; test.Value3 = new CloneableObject(); test.ValueArray4 = new CloneableObject[] { new CloneableObject() }; TestCloneableObject1 test2 = (TestCloneableObject1)test.Clone(); } } } 


सभी उदाहरणों में समान विशेषताएं हैं और विशेषताओं का क्लासिक उपयोग दिखाते हैं।
पेशेवरों:

विपक्ष:

एकीकरण


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

टेस्ट क्लास:
 using System; public class TestObject1 { [Clone, DataValue("Root/IntValue")] public int Value1; [DataValue("Root/StringValue")] public string Value2; [Clone, WidgetName("Test1")] public Widget Widget3; } 

परीक्षण वर्ग के लिए एक कंटेनर बनाया जाएगा, जिसके अंदर 3 इनिलाइज़र हैं:

शुरुआती के साथ परीक्षण वर्ग और निर्मित कंटेनर:



कार्यान्वयन


वर्तमान कार्यान्वयन एक डेमो है, किसी भी सिद्धांत का पालन नहीं करता है, त्रुटियों को संभालता नहीं है और थ्रेड सुरक्षित नहीं है। कार्यान्वयन का मुख्य उद्देश्य सिस्टम के संचालन को प्रदर्शित करना है।

आधार विशेषता वर्ग


क्षेत्र के लिए प्रतिनिधियों की पीढ़ी विशेषताओं के अंदर होती है। सभी उपयोगकर्ता विशेषताएँ FieldSetterAttribute विशेषता से विरासत में मिली हैं और जेनरेट विधि को ओवरराइड करती हैं, जहाँ प्रतिनिधि जनरेट किया जाता है और इनिशियलाइज़र सूची में जोड़ा जाता है।
स्रोत कोड
 using System; using System.Reflection; [AttributeUsage(AttributeTargets.Field, AllowMultiple = true)] public abstract class FieldSetterAttribute : Attribute { public abstract void Generate(Initialiser initialiser, FieldInfo info); } 


प्रारंभकर्ता


इनिशियलाइज़र का कार्य अपने आप में एक विशिष्ट इनिशियलाइज़ेशन सेट के प्रतिनिधियों की सूची को स्टोर करना है और मांग पर इसे निष्पादित करना है।
स्रोत कोड
 using System; using System.Collections.Generic; public delegate void InitialiseHandle(object target, object data); public class Initialiser { public void Add(InitialiseHandle handle) { handlers.Add(handle); } public void Initialise(object target, object data) { foreach (InitialiseHandle handler in handlers) handler(target, data); } private List<InitialiseHandle> handlers = new List<InitialiseHandle>(); } 


पात्र


कंटेनर में एक कस्टम वर्ग प्रकार के लिए सभी इनिशियलाइज़र होते हैं। प्रारंभकर्ता तक पहुंच विशेषता प्रकार से होती है।
स्रोत कोड
 using System; using System.Collections.Generic; public class Container { public Initialiser GetInitialiser(Type type) { Initialiser result; if (!initialisers.TryGetValue(type, out result)) { result = new Initialiser(); initialisers.Add(type, result); } return result; } private Dictionary<Type, Initialiser> initialisers = new Dictionary<Type, Initialiser>(); } 


मैनेजर टाइप करें


प्रकार प्रबंधक में संसाधित उपयोगकर्ता प्रकारों के लिए बनाए गए कंटेनर होते हैं। कंटेनरों का उत्पादन अनुरोध पर होता है और केवल एक बार किया जाता है।
स्रोत कोड
 using System; using System.Collections.Generic; using System.Reflection; public static class TypeManager { public static Container GetContainer(Type type) { Container result; if (!containers.TryGetValue(type, out result)) { result = new Container(); containers.Add(type, result); InitialiseContainer(type, result); } return result; } private static void InitialiseContainer(Type type, Container container) { FieldInfo[] fields = type.GetFields( BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo field in fields) { foreach (FieldSetterAttribute item in field.GetCustomAttributes( typeof(FieldSetterAttribute), false)) { Initialiser initialiser = container.GetInitialiser(item.GetType()); item.Generate(initialiser, field); } } } private static Dictionary<Type, Container> containers = new Dictionary<Type, Container>(); } 


कार्य सिद्धांत


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

कस्टम विशेषताएँ कार्यान्वयन उदाहरण


प्रणाली के सिद्धांतों को समझने के बाद, हम अपने परीक्षण प्रकार TestObject1 के लिए कस्टम विशेषताओं को लागू कर सकते हैं। नए परीक्षण प्रकार को TestObject2 कहा जाता है।
स्रोत कोड
टेस्ट क्लास कार्यान्वयन:
 using System; public class TestObject2 : Form, ICloneable { [Clone, DataValue("Root/IntValue")] public int Value1; [DataValue("Root/StringValue")] public string Value2; [Clone(Deep = true), WidgetName("Test1")] public Widget Widget3; public object Clone() { object clone = Activator.CreateInstance(GetType()); TypeManager.GetContainer(GetType()). GetInitialiser(typeof(CloneAttribute)).Initialise(clone, this); return clone; } } 

क्लोनिंग समर्थन के साथ एक विजेट का कार्यान्वयन:
 using System; public class Widget : ICloneable { public object Clone() { return new Widget(); } } 

फार्म का कार्यान्वयन:
 using System; public class Form { public void Initialise() { TypeManager.GetContainer(GetType()). GetInitialiser(typeof(WidgetNameAttribute)).Initialise(this, null); } public Widget FindWidget(string name) { return new Widget(); } } 

बाइंडिंग विशेषता विशेषता कार्यान्वयन:
 using System; using System.Reflection; [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)] public class WidgetNameAttribute : FieldSetterAttribute { public WidgetNameAttribute(string name) { this.name = name; } public override void Generate(Initialiser initialiser, FieldInfo info) { initialiser.Add( delegate(object target, object data) { Form targetForm = (Form)target; Widget widget = targetForm.FindWidget(name); info.SetValue(targetForm, widget); } ); } private string name; } 

क्रमांकन के लिए एक विशेषता का कार्यान्वयन:
 using System; using System.Reflection; using System.Xml; using System.Collections.Generic; [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)] public class DataValueAttribute : FieldSetterAttribute { static DataValueAttribute() { parsers[typeof(int)] = delegate(string value) { return int.Parse(value); }; parsers[typeof(string)] = delegate(string value) { return value; }; } public DataValueAttribute(string xPath) { this.xPath = xPath; } public override void Generate(Initialiser initialiser, FieldInfo info) { ParseHandle parser = parsers[info.FieldType]; initialiser.Add( delegate(object target, object data) { XmlNode node = ((XmlNode)data).SelectSingleNode(xPath); object value = parser(node.InnerText); info.SetValue(target, value); } ); } private string xPath; private delegate object ParseHandle(string value); private static Dictionary<Type, ParseHandle> parsers = new Dictionary<Type, ParseHandle>(); } 

क्लोनिंग के लिए विशेषता का कार्यान्वयन:
 using System; using System.Reflection; [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)] public class CloneAttribute : FieldSetterAttribute { public override void Generate(Initialiser initialiser, FieldInfo info) { if (deep) { if (info.FieldType.IsArray) { initialiser.Add( delegate(object target, object data) { object value = info.GetValue(data); Array oldArray = (Array)value; Array newArray = (Array)oldArray.Clone(); for (int index = 0; index < oldArray.Length; index++) newArray.SetValue(((ICloneable)oldArray.GetValue(index)). Clone(), index); value = newArray; info.SetValue(target, value); } ); } else { initialiser.Add( delegate(object target, object data) { object value = info.GetValue(data); value = ((ICloneable)value).Clone(); info.SetValue(target, value); } ); } } else { initialiser.Add( delegate(object target, object data) { object value = info.GetValue(data); info.SetValue(target, value); } ); } } public bool Deep { get { return deep; } set { deep = value; } } private bool deep; } 


परिणाम


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

Source: https://habr.com/ru/post/In168477/


All Articles