मैं C # में गतिशील गुण कैसे बनाऊं?


87

मैं स्थैतिक गुणों के एक सेट के साथ एक वर्ग बनाने के लिए रास्ता ढूंढ रहा हूं। रन टाइम में, मैं डेटाबेस से इस ऑब्जेक्ट में अन्य डायनेमिक गुण जोड़ना चाहता हूं। मैं इन वस्तुओं में छँटाई और फ़िल्टरिंग क्षमताओं को भी जोड़ना चाहता हूँ।

मैं इसे C # में कैसे करूँ?


3
इस वर्ग का उद्देश्य क्या है? आपका अनुरोध मुझे संदेह करता है कि आपको वास्तव में एक डिज़ाइन पैटर्न या कुछ और की आवश्यकता है, हालांकि यह नहीं जानते कि आपके उपयोग के मामले का क्या मतलब है कि मेरे पास वास्तव में कोई सुझाव नहीं है।
— ब्रायन

जवाबों:


59

आप एक शब्दकोश का उपयोग कर सकते हैं, कहते हैं

Dictionary<string,object> properties;

मुझे लगता है कि ज्यादातर मामलों में ऐसा ही कुछ किया जाता है, यह इस तरह से किया जाता है।
किसी भी मामले में, आपको सेट के साथ "वास्तविक" संपत्ति बनाने और एक्सेसर्स प्राप्त करने से कुछ भी हासिल नहीं होगा, क्योंकि यह केवल रन-टाइम पर बनाया जाएगा और आप इसे अपने कोड में उपयोग नहीं करेंगे ...

यहाँ एक उदाहरण है, फ़िल्टरिंग और सॉर्टिंग का संभावित कार्यान्वयन (कोई त्रुटि जाँच नहीं) दिखा रहा है:

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication1 {

    class ObjectWithProperties {
        Dictionary<string, object> properties = new Dictionary<string,object>();

        public object this[string name] {
            get { 
                if (properties.ContainsKey(name)){
                    return properties[name];
                }
                return null;
            }
            set {
                properties[name] = value;
            }
        }

    }

    class Comparer<T> : IComparer<ObjectWithProperties> where T : IComparable {

        string m_attributeName;

        public Comparer(string attributeName){
            m_attributeName = attributeName;
        }

        public int Compare(ObjectWithProperties x, ObjectWithProperties y) {
            return ((T)x[m_attributeName]).CompareTo((T)y[m_attributeName]);
        }

    }

    class Program {

        static void Main(string[] args) {

            // create some objects and fill a list
            var obj1 = new ObjectWithProperties();
            obj1["test"] = 100;
            var obj2 = new ObjectWithProperties();
            obj2["test"] = 200;
            var obj3 = new ObjectWithProperties();
            obj3["test"] = 150;
            var objects = new List<ObjectWithProperties>(new ObjectWithProperties[]{ obj1, obj2, obj3 });

            // filtering:
            Console.WriteLine("Filtering:");
            var filtered = from obj in objects
                         where (int)obj["test"] >= 150
                         select obj;
            foreach (var obj in filtered){
                Console.WriteLine(obj["test"]);
            }

            // sorting:
            Console.WriteLine("Sorting:");
            Comparer<int> c = new Comparer<int>("test");
            objects.Sort(c);
            foreach (var obj in objects) {
                Console.WriteLine(obj["test"]);
            }
        }

    }
}

30

यदि आपको डेटा-बाइंडिंग उद्देश्यों के लिए इसकी आवश्यकता है, तो आप इसे कस्टम डिस्क्रिप्टर मॉडल के साथ कर सकते हैं ... लागू करके ICustomTypeDescriptor, TypeDescriptionProviderऔर / या TypeCoverter, आप PropertyDescriptorरनटाइम पर अपने स्वयं के उदाहरण बना सकते हैं । यह वह है जैसे नियंत्रण DataGridView, PropertyGridआदि गुणों को प्रदर्शित करने के लिए उपयोग करते हैं।

सूचियों से बंधने के लिए, आपको आवश्यकता होगी ITypedListऔर IList; बुनियादी छँटाई के लिए IBindingList:; छानने और उन्नत छँटाई के लिए IBindingListView:; पूर्ण "नई पंक्ति" समर्थन के लिए ( DataGridView): ICancelAddNew(भतीजी!)।

हालांकि यह बहुत काम है। DataTable(हालांकि मुझे इससे नफरत है) वही काम करने का सस्ता तरीका है। यदि आपको डेटा-बाइंडिंग की आवश्यकता नहीं है, तो बस हैशटेबल का उपयोग करें; -पी

यहाँ एक सरल उदाहरण है - लेकिन आप बहुत कुछ कर सकते हैं ...


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

एक साइड नोट के रूप में, ICustomTypeDescriptor के माध्यम से डेटा बाइंडिंग Silverlight द्वारा समर्थित नहीं है :(।
— कर्ट हेगनलोचर

साइड नोट के साइड नोड के रूप में, सिल्वरलाइट 5 ने ICustomTypeDescriptor के स्थान पर ICustomTypeProvider इंटरफ़ेस पेश किया। ICustomTypeProvider को बाद में .NET फ्रेमवर्क 4.5 में पोर्ट किया गया था, सिल्वरलाइट और .NET फ्रेमवर्क के बीच पोर्टेबिलिटी की अनुमति देने के लिए। :)।
— एडवर्ड



12

मुझे यकीन नहीं है कि आप वास्तव में वह करना चाहते हैं जो आप कहते हैं कि आप करना चाहते हैं , लेकिन यह मेरे लिए नहीं है कि मैं इसका कारण बनूं!

JITed होने के बाद आप किसी वर्ग में गुण नहीं जोड़ सकते।

निकटतम आप प्राप्त कर सकते हैं गतिशील रूप से परावर्तन के साथ एक उपप्रकार बना सकते हैं। मौजूदा फ़ील्ड को कॉपी और कॉपी करें, लेकिन आपको ऑब्जेक्ट के सभी संदर्भों को स्वयं अपडेट करना होगा।

आप संकलन समय पर उन संपत्तियों तक पहुँचने में भी सक्षम नहीं होंगे।

कुछ इस तरह:

public class Dynamic
{
    public Dynamic Add<T>(string key, T value)
    {
        AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("DynamicAssembly"), AssemblyBuilderAccess.Run);
        ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("Dynamic.dll");
        TypeBuilder typeBuilder = moduleBuilder.DefineType(Guid.NewGuid().ToString());
        typeBuilder.SetParent(this.GetType());
        PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(key, PropertyAttributes.None, typeof(T), Type.EmptyTypes);

        MethodBuilder getMethodBuilder = typeBuilder.DefineMethod("get_" + key, MethodAttributes.Public, CallingConventions.HasThis, typeof(T), Type.EmptyTypes);
        ILGenerator getter = getMethodBuilder.GetILGenerator();
        getter.Emit(OpCodes.Ldarg_0);
        getter.Emit(OpCodes.Ldstr, key);
        getter.Emit(OpCodes.Callvirt, typeof(Dynamic).GetMethod("Get", BindingFlags.Instance | BindingFlags.NonPublic).MakeGenericMethod(typeof(T)));
        getter.Emit(OpCodes.Ret);
        propertyBuilder.SetGetMethod(getMethodBuilder);

        Type type = typeBuilder.CreateType();

        Dynamic child = (Dynamic)Activator.CreateInstance(type);
        child.dictionary = this.dictionary;
        dictionary.Add(key, value);
        return child;
    }

    protected T Get<T>(string key)
    {
        return (T)dictionary[key];
    }

    private Dictionary<string, object> dictionary = new Dictionary<string,object>();
}

मेरे पास इस मशीन पर वीएस स्थापित नहीं है, इसलिए मुझे बताएं कि क्या कोई बड़े पैमाने पर कीड़े हैं (अच्छी तरह से ... बड़े पैमाने पर प्रदर्शन की समस्याओं के अलावा, लेकिन मैंने विनिर्देश नहीं लिखा है!)

अब आप इसका उपयोग कर सकते हैं:

Dynamic d = new Dynamic();
d = d.Add("MyProperty", 42);
Console.WriteLine(d.GetType().GetProperty("MyProperty").GetValue(d, null));

आप इसे भाषा में एक सामान्य संपत्ति की तरह भी उपयोग कर सकते हैं जो देर से बाइंडिंग का समर्थन करती है (उदाहरण के लिए, VB.NET)


4

मैंने एक ICustomTypeDescriptor इंटरफ़ेस और एक शब्दकोश के साथ ठीक यही किया है।

गतिशील गुणों के लिए ICustomTypeDescriptor लागू करना:

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

यह प्रत्येक डेटा 'पंक्ति' को कुंजी के रूप में एक शब्दकोष के रूप में प्राप्त किया जा सकता है जिसमें संपत्ति का नाम और मूल्य एक स्ट्रिंग या एक वर्ग है जो निर्दिष्ट पंक्ति के लिए संपत्ति के मूल्य को संग्रहीत कर सकता है। निश्चित रूप से शब्दकोश की सूची वाली वस्तुएं ग्रिड के लिए बाध्य नहीं हो पाएंगी। यह वह जगह है जहाँ ICustomTypeDescriptor आता है।

डिक्शनरी के लिए एक रैपर क्लास बनाकर और उसे ICustomTypeDescriptor इंटरफ़ेस का पालन करने के लिए किसी ऑब्जेक्ट के लिए रिटर्निंग प्रॉपर्टीज़ के व्यवहार को ओवरराइड किया जा सकता है।

नीचे दिए गए डेटा 'पंक्ति' वर्ग के कार्यान्वयन पर एक नज़र डालें:

/// <summary>
/// Class to manage test result row data functions
/// </summary>
public class TestResultRowWrapper : Dictionary<string, TestResultValue>, ICustomTypeDescriptor
{
    //- METHODS -----------------------------------------------------------------------------------------------------------------

    #region Methods

    /// <summary>
    /// Gets the Attributes for the object
    /// </summary>
    AttributeCollection ICustomTypeDescriptor.GetAttributes()
    {
        return new AttributeCollection(null);
    }

    /// <summary>
    /// Gets the Class name
    /// </summary>
    string ICustomTypeDescriptor.GetClassName()
    {
        return null;
    }

    /// <summary>
    /// Gets the component Name
    /// </summary>
    string ICustomTypeDescriptor.GetComponentName()
    {
        return null;
    }

    /// <summary>
    /// Gets the Type Converter
    /// </summary>
    TypeConverter ICustomTypeDescriptor.GetConverter()
    {
        return null;
    }

    /// <summary>
    /// Gets the Default Event
    /// </summary>
    /// <returns></returns>
    EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
    {
        return null;
    }

    /// <summary>
    /// Gets the Default Property
    /// </summary>
    PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
    {
        return null;
    }

    /// <summary>
    /// Gets the Editor
    /// </summary>
    object ICustomTypeDescriptor.GetEditor(Type editorBaseType)
    {
        return null;
    }

    /// <summary>
    /// Gets the Events
    /// </summary>
    EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
    {
        return new EventDescriptorCollection(null);
    }

    /// <summary>
    /// Gets the events
    /// </summary>
    EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
    {
        return new EventDescriptorCollection(null);
    }

    /// <summary>
    /// Gets the properties
    /// </summary>
    PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
    {
        List<propertydescriptor> properties = new List<propertydescriptor>();

        //Add property descriptors for each entry in the dictionary
        foreach (string key in this.Keys)
        {
            properties.Add(new TestResultPropertyDescriptor(key));
        }

        //Get properties also belonging to this class also
        PropertyDescriptorCollection pdc = TypeDescriptor.GetProperties(this.GetType(), attributes);

        foreach (PropertyDescriptor oPropertyDescriptor in pdc)
        {
            properties.Add(oPropertyDescriptor);
        }

        return new PropertyDescriptorCollection(properties.ToArray());
    }

    /// <summary>
    /// gets the Properties
    /// </summary>
    PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
    {
        return ((ICustomTypeDescriptor)this).GetProperties(null);
    }

    /// <summary>
    /// Gets the property owner
    /// </summary>
    object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)
    {
        return this;
    }

    #endregion Methods

    //---------------------------------------------------------------------------------------------------------------------------
}

नोट: GetProperties विधि में मैं एक बार प्रदर्शन के लिए पढ़े गए प्रॉपर्टीस्क्रिप्ट को कैश कर सकता हूं लेकिन जैसा कि मैं रनटाइम में कॉलम जोड़ रहा हूं और निकाल रहा हूं मैं हमेशा उन्हें फिर से बनाना चाहता हूं।

आप GetProperties विधि में यह भी देखेंगे कि डिक्शनरी प्रविष्टियों के लिए जोड़े गए प्रॉपर्टी डेस्क्रिप्टर, TestResultPropertyDescriptor प्रकार के हैं। यह एक कस्टम प्रॉपर्टी डिस्क्रिप्टर वर्ग है जो यह बताता है कि प्रॉपर्टी को कैसे सेट किया जाता है और कैसे प्राप्त किया जाता है। नीचे दिए गए कार्यान्वयन पर एक नज़र डालें:

/// <summary>
/// Property Descriptor for Test Result Row Wrapper
/// </summary>
public class TestResultPropertyDescriptor : PropertyDescriptor
{
    //- PROPERTIES --------------------------------------------------------------------------------------------------------------

    #region Properties

    /// <summary>
    /// Component Type
    /// </summary>
    public override Type ComponentType
    {
        get { return typeof(Dictionary<string, TestResultValue>); }
    }

    /// <summary>
    /// Gets whether its read only
    /// </summary>
    public override bool IsReadOnly
    {
        get { return false; }
    }

    /// <summary>
    /// Gets the Property Type
    /// </summary>
    public override Type PropertyType
    {
        get { return typeof(string); }
    }

    #endregion Properties

    //- CONSTRUCTOR -------------------------------------------------------------------------------------------------------------

    #region Constructor

    /// <summary>
    /// Constructor
    /// </summary>
    public TestResultPropertyDescriptor(string key)
        : base(key, null)
    {

    }

    #endregion Constructor

    //- METHODS -----------------------------------------------------------------------------------------------------------------

    #region Methods

    /// <summary>
    /// Can Reset Value
    /// </summary>
    public override bool CanResetValue(object component)
    {
        return true;
    }

    /// <summary>
    /// Gets the Value
    /// </summary>
    public override object GetValue(object component)
    {
          return ((Dictionary<string, TestResultValue>)component)[base.Name].Value;
    }

    /// <summary>
    /// Resets the Value
    /// </summary>
    public override void ResetValue(object component)
    {
        ((Dictionary<string, TestResultValue>)component)[base.Name].Value = string.Empty;
    }

    /// <summary>
    /// Sets the value
    /// </summary>
    public override void SetValue(object component, object value)
    {
        ((Dictionary<string, TestResultValue>)component)[base.Name].Value = value.ToString();
    }

    /// <summary>
    /// Gets whether the value should be serialized
    /// </summary>
    public override bool ShouldSerializeValue(object component)
    {
        return false;
    }

    #endregion Methods

    //---------------------------------------------------------------------------------------------------------------------------
}

इस वर्ग को देखने के लिए मुख्य गुण GetValue और SetValue हैं। यहाँ आप एक डिक्शनरी के रूप में डाले जा रहे घटक को देख सकते हैं और इसके अंदर मौजूद कुंजी का मूल्य सेट या पुनः प्राप्त किया जा सकता है। इसका महत्त्वपूर्ण यह है कि इस वर्ग में शब्दकोश पंक्ति के आवरण वर्ग में एक ही प्रकार का है अन्यथा कलाकार विफल हो जाएगा। जब डिस्क्रिप्टर बनाया जाता है तो कुंजी (संपत्ति का नाम) पारित हो जाती है और सही मूल्य प्राप्त करने के लिए शब्दकोश को क्वेरी करने के लिए उपयोग किया जाता है।

मेरे ब्लॉग से लिया गया:

गतिशील गुणों के लिए ICustomTypeDescriptor कार्यान्वयन


मुझे पता है कि आपने इसे हमेशा के लिए लिखा था, लेकिन आपको वास्तव में अपने उत्तर में अपना कुछ कोड डालना चाहिए, या अपने पोस्ट से कुछ उद्धृत करना चाहिए। मुझे लगता है कि नियमों में - आपका उत्तर लगभग निरर्थक हो जाता है यदि आपका लिंक अंधेरा हो गया था। हालांकि डाउनवोट नहीं जा रहे हैं क्योंकि आप MSDN ( msdn.microsoft.com/en-us/library/… ) पर ICustomTypeDescriptor देख सकते हैं
— डेविड Schwartz

@DavidSchwartz - जोड़ा गया।
— व्रतनाथ

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

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

1

आपको डिपेंडेंसीऑब्जेक्ट में देखना चाहिए क्योंकि WPF द्वारा उपयोग किया जाता है ये एक समान पैटर्न का पालन करते हैं जिसके तहत रनटाइम पर गुण असाइन किए जा सकते हैं। जैसा कि ऊपर उल्लेख किया गया है, अंततः हैश तालिका का उपयोग करने की ओर इशारा करता है।

एक अन्य उपयोगी चीज CSLA.Net पर एक नज़र है । कोड स्वतंत्र रूप से उपलब्ध है और आपके द्वारा दिखाई देने वाले कुछ सिद्धांतों के पैटर्न का उपयोग करता है।

इसके अलावा अगर आप छँटाई और छान रहे हैं तो मुझे लगता है कि आप किसी तरह के ग्रिड का उपयोग करने जा रहे हैं। कार्यान्वित करने के लिए एक उपयोगी इंटरफ़ेस ICustomTypeDescriptor है, इससे आप प्रभावी ढंग से ओवरराइड कर सकते हैं जब आपकी वस्तु परिलक्षित होती है तो आप परावर्तक को अपनी आंतरिक हैश तालिका में इंगित कर सकते हैं।


1

कुछ orsogufo के कोड के प्रतिस्थापन के रूप में, क्योंकि मैं हाल ही में इस समस्या के लिए खुद एक शब्दकोश लेकर गया था, यहाँ मेरा [ऑपरेटर] है:

public string this[string key]
{
    get { return properties.ContainsKey(key) ? properties[key] : null; }

    set
    {
        if (properties.ContainsKey(key))
        {
            properties[key] = value;
        }
        else
        {
            properties.Add(key, value);
        }
    }
}

इस कार्यान्वयन के साथ, सेटर नए कुंजी-मूल्य जोड़े जोड़ देगा जब आप उपयोग करेंगे []=यदि वे पहले से ही शब्दकोश में मौजूद नहीं हैं।

इसके अलावा, मेरे propertiesलिए एक है IDictionaryऔर कंस्ट्रक्टर्स में मैं इसे इनिशियलाइज़ करता हूं new SortedDictionary<string, string>()।


मैं आपके समाधान की कोशिश कर रहा हूं। मैं के रूप में सेवा पक्ष में एक मूल्य निर्धारित कर रहा हूँ record[name_column] = DBConvert.To<string>(r[name_column]);जहाँ recordमेरी डीटीओ है। मैं ग्राहक मूल्य पर यह मूल्य कैसे प्राप्त करूं?
— रोहन

1

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


0

इंडेक्सर को पास किए गए स्ट्रिंग मान के रूप में गुण नाम के साथ एक इंडेक्सर का उपयोग क्यों नहीं किया जाता है?


0

क्या आप अपनी कक्षा को एक शब्दकोश वस्तु को उजागर नहीं कर सकते थे? "ऑब्जेक्ट में अधिक गुण संलग्न करने" के बजाय, आप केवल रन समय पर शब्दकोश में अपना डेटा (कुछ पहचानकर्ता के साथ) डाल सकते हैं।


0

यदि यह बाध्यकारी के लिए है, तो आप XAML से इंडेक्सर्स को संदर्भित कर सकते हैं

Text="{Binding [FullName]}"

यहाँ यह कुंजी "FullName" के साथ वर्ग सूचकांक का संदर्भ दे रहा है

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