c# - मैं सामान्य विधि को कॉल करने के लिए प्रतिबिंब का उपयोग कैसे करूं?




generics reflection (5)

मूल उत्तर के लिए बस एक जोड़ा। हालांकि यह काम करेगा:

MethodInfo method = typeof(Sample).GetMethod("GenericMethod");
MethodInfo generic = method.MakeGenericMethod(myType);
generic.Invoke(this, null);

यह भी थोड़ा खतरनाक है कि आप GenericMethod लिए संकलन-समय की जांच खो देते हैं। यदि आप बाद में एक रिफैक्टरिंग करते हैं और GenericMethod नाम GenericMethod , तो यह कोड नोटिस नहीं करेगा और रन टाइम पर विफल हो जाएगा। इसके अलावा, यदि असेंबली के बाद कोई प्रसंस्करण है (उदाहरण के लिए अप्रयुक्त विधियों / कक्षाओं को रोकना या निकालना) यह कोड भी तोड़ सकता है।

इसलिए, यदि आप उस विधि को जानते हैं जिसे आप संकलित समय से जोड़ रहे हैं, और इसे लाखों बार नहीं कहा जाता है तो ओवरहेड कोई फर्क नहीं पड़ता, मैं इस कोड को बदल दूंगा:

Action<> GenMethod = GenericMethod<int>;  //change int by any base type 
                                          //accepted by GenericMethod
MethodInfo method = this.GetType().GetMethod(GenMethod.Method.Name);
MethodInfo generic = method.MakeGenericMethod(myType);
generic.Invoke(this, null);

हालांकि बहुत सुंदर नहीं है, आपके पास GenericMethod लिए संकलन समय संदर्भ है, और यदि आप GenericMethod साथ कुछ भी रिफैक्टर, डिलीट या डिलीट करते हैं, तो यह कोड काम करेगा, या कम से कम संकलन समय पर टूट जाएगा (उदाहरण के लिए यदि आप GenericMethod को GenericMethod )।

ऐसा करने का दूसरा तरीका एक नया रैपर वर्ग बनाना होगा, और इसे Activator माध्यम से बनाना होगा। मुझे नहीं पता कि कोई बेहतर तरीका है या नहीं।

टाइप पैरामीटर संकलन समय पर ज्ञात नहीं होने पर सामान्य विधि को कॉल करने का सबसे अच्छा तरीका क्या है, लेकिन इसके बजाय रनटाइम पर गतिशील रूप से प्राप्त किया जाता है?

निम्नलिखित नमूना कोड पर विचार करें - Example() विधि के अंदर, myType चर में संग्रहीत Type का उपयोग कर GenericMethod<T>() को आमंत्रित करने का सबसे संक्षिप्त तरीका क्या है?

public class Sample
{
    public void Example(string typeName)
    {
        Type myType = FindType(typeName);

        // What goes here to call GenericMethod<T>()?
        GenericMethod<myType>(); // This doesn't work

        // What changes to call StaticMethod<T>()?
        Sample.StaticMethod<myType>(); // This also doesn't work
    }

    public void GenericMethod<T>()
    {
        // ...
    }

    public static void StaticMethod<T>()
    {
        //...
    }
}

यह ग्राक्स के उत्तर के आधार पर मेरा 2 सेंट है, लेकिन एक सामान्य विधि के लिए आवश्यक दो पैरामीटर के साथ।

मान लें कि आपकी विधि को हेल्पर्स क्लास में निम्नानुसार परिभाषित किया गया है:

public class Helpers
{
    public static U ConvertCsvDataToCollection<U, T>(string csvData)
    where U : ObservableCollection<T>
    {
      //transform code here
    }
}

मेरे मामले में, यू प्रकार हमेशा एक अवलोकन संग्रह प्रकार टी की वस्तु भंडारण है।

जैसा कि मेरे प्रकार पूर्वनिर्धारित हैं, मैं पहले "डमी" ऑब्जेक्ट्स बनाता हूं जो देखने योग्य संग्रह (यू) और उसमें संग्रहीत ऑब्जेक्ट (टी) का प्रतिनिधित्व करते हैं और मेक को कॉल करते समय उनका प्रकार प्राप्त करने के लिए नीचे उपयोग किया जाएगा

object myCollection = Activator.CreateInstance(collectionType);
object myoObject = Activator.CreateInstance(objectType);

फिर अपने जेनेरिक फ़ंक्शन को ढूंढने के लिए GetMethod को कॉल करें:

MethodInfo method = typeof(Helpers).
GetMethod("ConvertCsvDataToCollection");

अब तक, उपरोक्त कॉल उपरोक्त समझाया गया था, लेकिन एक छोटे से अंतर के साथ, जब आपको इसे कई पैरामीटर पास करना होगा।

आपको MakeGenericMethod फ़ंक्शन में एक प्रकार [] सरणी पारित करने की आवश्यकता है जिसमें "डमी" ऑब्जेक्ट्स प्रकार शामिल हैं जो ऊपर बनाए गए थे:

MethodInfo generic = method.MakeGenericMethod(
new Type[] {
   myCollection.GetType(),
   myObject.GetType()
});

एक बार ऐसा करने के बाद, आपको उपरोक्त वर्णित Invoke विधि को कॉल करने की आवश्यकता है।

generic.Invoke(null, new object[] { csvData });

और आपने कल लिया। एक आकर्षण काम करता है!

अद्यतन करें:

जैसा कि @ बेवन ने हाइलाइट किया है, मुझे मेकजेनरिक विधि फ़ंक्शन को कॉलम के रूप में कॉल करते समय सरणी बनाने की आवश्यकता नहीं है क्योंकि मुझे प्रकार प्राप्त करने के लिए ऑब्जेक्ट बनाने की आवश्यकता नहीं है क्योंकि मैं सीधे इस फ़ंक्शन पर प्रकारों को पास कर सकता हूं। मेरे मामले में, चूंकि मेरे पास किसी अन्य वर्ग में पूर्वनिर्धारित प्रकार हैं, इसलिए मैंने बस अपना कोड बदल दिया:

object myCollection = null;

MethodInfo method = typeof(Helpers).
GetMethod("ConvertCsvDataToCollection");

MethodInfo generic = method.MakeGenericMethod(
   myClassInfo.CollectionType,
   myClassInfo.ObjectType
);

myCollection = generic.Invoke(null, new object[] { csvData });

myClassInfo में टाइप Type 2 गुण होते हैं जिन्हें मैंने कन्स्ट्रक्टर को पास किए गए एनम वैल्यू के आधार पर रन टाइम पर सेट किया है और मुझे प्रासंगिक प्रकार प्रदान करेगा जो मैं MakeGenericMethod में उपयोग करता हूं।

इस @ बेवन को हाइलाइट करने के लिए फिर से धन्यवाद।


विधि को प्रारंभ करने के लिए आपको प्रतिबिंब का उपयोग करने की आवश्यकता है, फिर MakeGenericMethod साथ प्रकार तर्कों की आपूर्ति करके इसे "निर्माण" करें:

MethodInfo method = typeof(Sample).GetMethod("GenericMethod");
MethodInfo generic = method.MakeGenericMethod(myType);
generic.Invoke(this, null);

एक स्थैतिक विधि के लिए, Invoke करने के लिए पहले तर्क के रूप में null को पास करें। जेनेरिक तरीकों से ऐसा करने के लिए कुछ भी नहीं है - यह सिर्फ सामान्य प्रतिबिंब है।

जैसा कि ध्यान दिया गया है, यह बहुत सी dynamic का उपयोग कर सी # 4 के रूप में सरल है - यदि आप निश्चित रूप से टाइप अनुमान का उपयोग कर सकते हैं। यह उन मामलों में मदद नहीं करता है जहां टाइपेंस अनुमान उपलब्ध नहीं है, जैसे प्रश्न में सटीक उदाहरण।


सी # 4.0 के साथ, प्रतिबिंब आवश्यक नहीं है क्योंकि डीएलआर रनटाइम प्रकारों का उपयोग कर इसे कॉल कर सकता है। चूंकि डीएलआर लाइब्रेरी का उपयोग करने से गतिशील रूप से दर्द होता है (आपके लिए सी # कंपाइलर जनरेटिंग कोड की बजाय), ओपन सोर्स फ्रेमवर्क Dynamitey (.नेट मानक 1.5) आपको एक ही कॉल के लिए आसान कैश रन-टाइम एक्सेस देता है जो संकलक उत्पन्न करेगा तुम्हारे लिए।

var name = InvokeMemberName.Create;
Dynamic.InvokeMemberAction(this, name("GenericMethod", new[]{myType}));


var staticContext = InvokeContext.CreateStatic;
Dynamic.InvokeMemberAction(staticContext(typeof(Sample)), name("StaticMethod", new[]{myType}));

किसी ने " क्लासिक प्रतिबिंब " समाधान प्रदान नहीं किया है, इसलिए यहां एक पूर्ण कोड उदाहरण है:

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

namespace DictionaryRuntime
{
    public class DynamicDictionaryFactory
    {
        /// <summary>
        /// Factory to create dynamically a generic Dictionary.
        /// </summary>
        public IDictionary CreateDynamicGenericInstance(Type keyType, Type valueType)
        {
            //Creating the Dictionary.
            Type typeDict = typeof(Dictionary<,>);

            //Creating KeyValue Type for Dictionary.
            Type[] typeArgs = { keyType, valueType };

            //Passing the Type and create Dictionary Type.
            Type genericType = typeDict.MakeGenericType(typeArgs);

            //Creating Instance for Dictionary<K,T>.
            IDictionary d = Activator.CreateInstance(genericType) as IDictionary;

            return d;

        }
    }
}

उपरोक्त DynamicDictionaryFactory कक्षा में एक विधि है

CreateDynamicGenericInstance(Type keyType, Type valueType)

और यह एक IDictionary उदाहरण बनाता है और देता है, जिनकी चाबियाँ और मान किस प्रकार के कॉल keyType और valueType keyType पर निर्दिष्ट हैं।

यहां एक पूर्ण उदाहरण दिया गया है कि इस विधि को तुरंत चालू करने और Dictionary<String, int> उपयोग करने के लिए कैसे करें Dictionary<String, int> :

using System;
using System.Collections.Generic;

namespace DynamicDictionary
{
    class Test
    {
        static void Main(string[] args)
        {
            var factory = new DictionaryRuntime.DynamicDictionaryFactory();
            var dict = factory.CreateDynamicGenericInstance(typeof(String), typeof(int));

            var typedDict = dict as Dictionary<String, int>;

            if (typedDict != null)
            {
                Console.WriteLine("Dictionary<String, int>");

                typedDict.Add("One", 1);
                typedDict.Add("Two", 2);
                typedDict.Add("Three", 3);

                foreach(var kvp in typedDict)
                {
                    Console.WriteLine("\"" + kvp.Key + "\": " + kvp.Value);
                }
            }
            else
                Console.WriteLine("null");
        }
    }
}

जब उपरोक्त कंसोल एप्लिकेशन निष्पादित किया जाता है, तो हमें सही, अपेक्षित परिणाम मिलते हैं:

Dictionary<String, int>
"One": 1
"Two": 2
"Three": 3




reflection