জেনেরিক পদ্ধতিতে কল করতে আমি কীভাবে প্রতিবিম্ব ব্যবহার করব?


1069

টাইপ প্যারামিটার সংকলনের সময় জানা না গেলে জেনেরিক পদ্ধতিতে কল করার সর্বোত্তম উপায় কী, তবে পরিবর্তে রানটাইমে গতিশীলভাবে প্রাপ্ত হয়?

নিম্নলিখিত নমুনা কোডটি বিবেচনা করুন - Example()পদ্ধতির অভ্যন্তরে, ভেরিয়েবলের মধ্যে সঞ্চিত GenericMethod<T>()ব্যবহার করে প্রার্থনা করার সবচেয়ে সংক্ষিপ্ত উপায় কোনটি ?TypemyType

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>()
    {
        //...
    }
}

7
আমি জনের সমাধান চেষ্টা করেছিলাম এবং জেনেরিক পদ্ধতিটি আমার ক্লাসে প্রকাশ না করা পর্যন্ত এটি কাজ করতে পারিনি। আমি জানি যে আরেক জন জনের উত্তর দিয়েছিল যে আপনাকে বাইন্ডিংফ্লাগগুলি নির্দিষ্ট করতে হবে তবে এটি কোনও সাহায্য করেনি।
নাস্কেউ

12
ব্যক্তিগত / অভ্যন্তরীণ পদ্ধতিটি পেতে আপনার BindingFlags.Instanceকেবল প্রয়োজনও নয় BindingFlags.NonPublic
লার্স কেমম্যান

2
এই প্রশ্নের আধুনিক সংস্করণ: stackoverflow.com/q/2433436/103167
বেন ভয়েগট

@ পিটার মর্টেনসেন - fyi আমি 'এর আগে ফাঁকা জায়গা ব্যবহার করেছি?' ইংরেজি অংশগুলি অ-ইংলিশ (সি #) অংশ থেকে আলাদা করতে; আইএমএইচও স্থানটি অপসারণের মতো দেখায়? কোড অংশ। যদি কোনও কোড না থাকে তবে আমি অবশ্যই স্পেসগুলি সরিয়ে নেওয়ার সাথে একমত হব, তবে এই ক্ষেত্রে ...
বেভেন

উত্তর:


1137

পদ্ধতিটি শুরু করার জন্য আপনাকে প্রতিবিম্বটি ব্যবহার করতে হবে, তারপরে মেকজেনারিকমেথোডের সাথে টাইপ আর্গুমেন্ট সরবরাহ করে এটি "নির্মাণ" করুন :

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

স্থির পদ্ধতির জন্য, nullপ্রথম আর্গুমেন্ট হিসাবে পাস করুন Invoke। জেনেরিক পদ্ধতিগুলির সাথে এটি করার কিছুই নেই - এটি কেবল সাধারণ প্রতিচ্ছবি।

যেমনটি উল্লেখ করা হয়েছে, এর অনেকগুলি সি # 4 হিসাবে সহজ ব্যবহার করে dynamic- যদি আপনি অবশ্যই টাইপ অনুমিতি ব্যবহার করতে পারেন। প্রকারের অনুক্রমের ক্ষেত্রে যেমন টাইপ অনুমান পাওয়া যায় না, যেমন প্রশ্নে সঠিক উদাহরণ হিসাবে এটি সহায়তা করে না।


92
+1 টি; নোট করুন যে GetMethod()কেবলমাত্র সর্বজনীন উদাহরণ পদ্ধতি ডিফল্টরূপে বিবেচনা করে, সুতরাং আপনার প্রয়োজন হতে পারে BindingFlags.Staticএবং / অথবা BindingFlags.NonPublic

20
পতাকাগুলির সঠিক সংমিশ্রণটি BindingFlags.NonPublic | BindingFlags.Instance(এবং optionচ্ছিকভাবে BindingFlags.Static)।
লার্স কেমম্যান

4
স্থিতিশীল পদ্ধতিগুলির সাথে এটি কীভাবে করা যায় - এবং প্রযুক্তিগতভাবে এখানে প্রশ্নটি এখানে আসে তার একটি প্রশ্ন চিহ্নিত হয়ে উঠছে ers জেনেরিক.আনভোক () এর প্রথম প্যারামিটার স্থির পদ্ধতিতে কল করার সময় শূন্য হওয়া উচিত। প্রথম প্যারামিটারটি কেবলমাত্র প্রয়োজনীয় পরিস্থিতিতে কল করার সময় প্রয়োজনীয়।
ক্রিস মোসচিনি

2
@ ক্রিসমোসচিনি: উত্তরে এটি যুক্ত হয়েছে।
জন স্কিটি

2
@ গজৌ: আমি উত্তরে কিছু যুক্ত করেছি - তবে মনে রাখবেন যে প্রশ্নের জেনেরিক পদ্ধতিগুলি কল করার জন্য , সহায়তা dynamicকরে না কারণ টাইপ অনুমান পাওয়া যায় না। (ধরণের আর্গুমেন্ট নির্ধারণ করতে সংকলকটি ব্যবহার করতে পারে এমন কোনও যুক্তি নেই))
জন স্কিটি

170

আসল উত্তরের সাথে কেবল একটি সংযোজন। যদিও এটি কাজ করবে:

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

এটি আপনার জন্য সংকলন-সময় চেক হারাতে কিছুটা বিপজ্জনক 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)।

এটি করার অন্যান্য উপায় হ'ল একটি নতুন র‌্যাপার ক্লাস তৈরি করা এবং এর মাধ্যমে তৈরি করা Activator। এর চেয়ে ভাল উপায় আছে কিনা জানি না।


5
যে পদ্ধতিগুলিতে প্রতিবিম্বটি কোনও পদ্ধতিতে কল করার জন্য ব্যবহৃত হয়, এটি স্বাভাবিক যে পদ্ধতিটির নামটি নিজেই অন্য কোনও পদ্ধতি দ্বারা আবিষ্কার করা হয়েছিল। পদ্ধতির নাম আগেই জানা সাধারণ নয়।
বেভান

13
ঠিক আছে, আমি প্রতিবিম্বের সাধারণ ব্যবহারগুলির জন্য সম্মত। তবে আসল প্রশ্নটি ছিল "জেনেরিকমেথড <myType> ()" কীভাবে কল করতে হবে যদি সেই সিনট্যাক্সের অনুমতি দেওয়া হত তবে আমাদের getMethod () এর মোটেই দরকার পড়েনি। তবে "আমি কীভাবে" GenericMethod <myType> "লিখব? আমি মনে করি উত্তরটি জেনেরিকমেথোডের সাথে সংকলন-সময় লিঙ্কটি এড়ানো থেকে বাঁচার একটি উপায় অন্তর্ভুক্ত করা উচিত। এখন এই প্রশ্নটি যদি সাধারণ হয় বা না হয় তবে আমি জানি না, তবে আমি জানি যে গতকাল আমার এই সঠিক সমস্যাটি ছিল এবং সে কারণেই আমি এই প্রশ্নে অবতীর্ণ হয়েছিলাম
অ্যাড্রিয়ান গ্যালেরো

20
আপনি GenMethod.Method.GetGenericMethodDefinition()পরিবর্তে করতে পারে this.GetType().GetMethod(GenMethod.Method.Name)। এটি সামান্য পরিষ্কার এবং সম্ভবত নিরাপদ।
ড্যানিয়েল ক্যাসিডি

আপনার নমুনায় "মাইটাইপ" অর্থ কী?
বিকাশকারী

37
এখন আপনি ব্যবহার করতে পারেনnameof(GenericMethod)
dmigo

140

শুধুমাত্র রানটাইমের সময় পরিচিত টাইপ প্যারামিটারের সাথে জেনেরিক পদ্ধতিতে কলিংকে dynamicপ্রতিবিম্ব এপিআইয়ের পরিবর্তে কোনও প্রকার ব্যবহার করে খুব সহজ করা যায় ।

এই কৌশলটি ব্যবহার করতে টাইপটি প্রকৃত অবজেক্ট ( Typeশ্রেণীর উদাহরণ নয় ) থেকে অবশ্যই জানা উচিত । অন্যথায়, আপনাকে সেই ধরণের একটি অবজেক্ট তৈরি করতে হবে বা স্ট্যান্ডার্ড প্রতিবিম্ব এপিআই সমাধান ব্যবহার করতে হবে । আপনি অ্যাক্টিভেটর.ক্রিয়েটআইনস্ট্যান্স পদ্ধতিটি ব্যবহার করে একটি অবজেক্ট তৈরি করতে পারেন ।

আপনি যদি জেনেরিক পদ্ধতিতে কল করতে চান, "সাধারণ" ব্যবহারের ক্ষেত্রে তার ধরণের অনুমান করা হত, তবে এটি কেবল অজানা টাইপের অবজেক্টে কাস্টিংয়ের জন্য আসে dynamic। এখানে একটি উদাহরণ:

class Alpha { }
class Beta { }
class Service
{
    public void Process<T>(T item)
    {
        Console.WriteLine("item.GetType(): " + item.GetType()
                          + "\ttypeof(T): " + typeof(T));
    }
}

class Program
{
    static void Main(string[] args)
    {
        var a = new Alpha();
        var b = new Beta();

        var service = new Service();
        service.Process(a); // Same as "service.Process<Alpha>(a)"
        service.Process(b); // Same as "service.Process<Beta>(b)"

        var objects = new object[] { a, b };
        foreach (var o in objects)
        {
            service.Process(o); // Same as "service.Process<object>(o)"
        }
        foreach (var o in objects)
        {
            dynamic dynObj = o;
            service.Process(dynObj); // Or write "service.Process((dynamic)o)"
        }
    }
}

এবং এই প্রোগ্রামটির ফলাফল এখানে:

item.GetType(): Alpha    typeof(T): Alpha
item.GetType(): Beta     typeof(T): Beta
item.GetType(): Alpha    typeof(T): System.Object
item.GetType(): Beta     typeof(T): System.Object
item.GetType(): Alpha    typeof(T): Alpha
item.GetType(): Beta     typeof(T): Beta

Processএকটি জেনেরিক উদাহরণ পদ্ধতি যা পাস করা আর্গুমেন্টের আসল ধরণ ( GetType()পদ্ধতিটি ব্যবহার করে ) এবং জেনেরিক প্যারামিটারের ধরণ ( typeofঅপারেটর ব্যবহার করে ) লিখে দেয় ।

dynamicটাইপ করার জন্য অবজেক্ট আর্গুমেন্টটি ingালাইয়ের মাধ্যমে আমরা রানটাইম পর্যন্ত টাইপ প্যারামিটার সরবরাহ স্থগিত করেছি। Processপদ্ধতিটি যখন dynamicআর্গুমেন্টের সাথে ডাকা হয় তখন সংকলক এই যুক্তির ধরণের বিষয়ে চিন্তা করে না। সংকলক কোডটি উত্পন্ন করে যা রানটাইমের সময় প্রকৃত প্রকারের আর্গুমেন্টগুলি (প্রতিবিম্ব ব্যবহার করে) পরীক্ষা করে এবং কল করার জন্য সেরা পদ্ধতিটি চয়ন করে। এখানে এখানে কেবলমাত্র একটি জেনেরিক পদ্ধতি রয়েছে, সুতরাং এটি একটি সঠিক ধরণের পরামিতি সহ আহ্বান করা হয়েছে।

এই উদাহরণে, আউটপুট একই হিসাবে আপনি লিখেছেন:

foreach (var o in objects)
{
    MethodInfo method = typeof(Service).GetMethod("Process");
    MethodInfo generic = method.MakeGenericMethod(o.GetType());
    generic.Invoke(service, new object[] { o });
}

ডায়নামিক টাইপের সংস্করণটি অবশ্যই সংক্ষিপ্ত এবং লেখা সহজ। এই ফাংশনটি একাধিকবার কল করার পারফরম্যান্স সম্পর্কেও আপনার চিন্তা করা উচিত নয়। একই ধরণের আর্গুমেন্ট সহ পরবর্তী কলটি ডিএলআর -তে ক্যাশিং ব্যবস্থার জন্য দ্রুত ধন্যবাদ হওয়া উচিত । অবশ্যই, আপনি কোডটি লিখতে পারেন যা ক্যাশে প্রতিনিধিদের আহ্বান জানিয়েছিল, তবে dynamicপ্রকারটি ব্যবহার করে আপনি এই আচরণটি বিনামূল্যে পান।

আপনি যে জেনেরিক পদ্ধতিতে কল করতে চান তাতে যদি প্যারামাইট্রাইজড ধরণের আর্গুমেন্ট না থাকে (সুতরাং এর ধরণের প্যারামিটারটি অনুমান করা যায় না) তবে আপনি জেনেরিক পদ্ধতির অনুরোধটিকে একটি সহায়ক পদ্ধতিতে মোড়ানো করতে পারেন নীচের উদাহরণের মতো:

class Program
{
    static void Main(string[] args)
    {
        object obj = new Alpha();

        Helper((dynamic)obj);
    }

    public static void Helper<T>(T obj)
    {
        GenericMethod<T>();
    }

    public static void GenericMethod<T>()
    {
        Console.WriteLine("GenericMethod<" + typeof(T) + ">");
    }
}

প্রকারের সুরক্ষা বৃদ্ধি করা

dynamicপ্রতিবিম্ব API ব্যবহারের জন্য প্রতিস্থাপন হিসাবে অবজেক্টটি ব্যবহার করার ক্ষেত্রে যা দুর্দান্ত তা হল আপনি কেবল রানটাইম অবধি জানেন না এমন এই নির্দিষ্ট ধরণের সংকলনের সময় পরীক্ষাটি হারাবেন। অন্যান্য যুক্তি এবং পদ্ধতির নামটি যথাযথভাবে সংকলক দ্বারা বিশ্লেষণ করা হয়। আপনি যদি আরও যুক্তি সরিয়ে বা যুক্ত করেন, তাদের ধরন পরিবর্তন করুন বা পদ্ধতিটির নাম পরিবর্তন করুন তবে আপনি একটি সংকলন-সময় ত্রুটি পাবেন। আপনি যদি স্ট্রিং হিসাবে পদ্ধতির নাম Type.GetMethodএবং অবজেক্টগুলিকে অ্যারে হিসাবে আর্গুমেন্ট সরবরাহ করেন তবে এটি ঘটবে না MethodInfo.Invoke

নীচে একটি সহজ উদাহরণ দেওয়া আছে যা বোঝায় যে সংকলনের সময় (মন্তব্য কোড) এবং রানটাইমের সময় কিছু ত্রুটি কীভাবে ধরা যেতে পারে। এটিও দেখায় যে কীভাবে DLR কল করতে হবে তা সমাধান করার চেষ্টা করে।

interface IItem { }
class FooItem : IItem { }
class BarItem : IItem { }
class Alpha { }

class Program
{
    static void Main(string[] args)
    {
        var objects = new object[] { new FooItem(), new BarItem(), new Alpha() };
        for (int i = 0; i < objects.Length; i++)
        {
            ProcessItem((dynamic)objects[i], "test" + i, i);

            //ProcesItm((dynamic)objects[i], "test" + i, i);
            //compiler error: The name 'ProcesItm' does not
            //exist in the current context

            //ProcessItem((dynamic)objects[i], "test" + i);
            //error: No overload for method 'ProcessItem' takes 2 arguments
        }
    }

    static string ProcessItem<T>(T item, string text, int number)
        where T : IItem
    {
        Console.WriteLine("Generic ProcessItem<{0}>, text {1}, number:{2}",
                          typeof(T), text, number);
        return "OK";
    }
    static void ProcessItem(BarItem item, string text, int number)
    {
        Console.WriteLine("ProcessItem with Bar, " + text + ", " + number);
    }
}

এখানে আমরা আবার কোনও dynamicধরণের আর্গুমেন্ট কাস্ট করে কিছু পদ্ধতি কার্যকর করি । প্রথম আর্গুমেন্টের ধরণের কেবল যাচাইকরণটি রানটাইম স্থগিত করা হয়। আপনি যে পদ্ধতিতে কল করছেন তার নামটি উপস্থিত না থাকলে বা অন্য যুক্তি অবৈধ (আর্গুমেন্টের ভুল সংখ্যা বা ভুল ধরণের) থাকলে আপনি একটি সংকলক ত্রুটি পাবেন।

আপনি যখন dynamicকোনও পদ্ধতিতে যুক্তিটি পাস করেন তখন এই কলটি ইদানীং সীমাবদ্ধ । পদ্ধতির ওভারলোড রেজোলিউশন রানটাইমে ঘটে এবং সেরা ওভারলোড চয়ন করার চেষ্টা করে। সুতরাং আপনি যদি ProcessItemকোনও BarItemধরণের অবজেক্টের সাথে পদ্ধতিটি আহ্বান করেন তবে আপনি প্রকৃতপক্ষে অ-জেনেরিক পদ্ধতিটি কল করবেন কারণ এটি এই ধরণের জন্য আরও ভাল ম্যাচ। যাইহোক, আপনি যখন Alphaটাইপের একটি যুক্তি পাস করেন তখন আপনি রানটাইম ত্রুটি পাবেন কারণ এই বিষয়টিকে হ্যান্ডেল করতে পারে এমন কোনও পদ্ধতি নেই (একটি জেনেরিক পদ্ধতির সীমাবদ্ধতা রয়েছে where T : IItemএবং Alphaশ্রেণি এই ইন্টারফেসটি প্রয়োগ করে না)। তবে এটাই পুরো কথা। সংকলকটির কাছে এই কলটি বৈধ কিনা সেই তথ্য নেই। প্রোগ্রামার হিসাবে আপনি এটি জানেন এবং আপনার এই কোডটি ত্রুটি ছাড়াই চলে কিনা তা নিশ্চিত হওয়া উচিত।

রিটার্ন টাইপ

আপনি গতিশীল ধরনের একটি প্যারামিটার সহ একটি অ অকার্যকর পদ্ধতি আহ্বান করছি, তখন তার রিটার্ন টাইপ সম্ভবত হবে হতে dynamicখুব । সুতরাং আপনি যদি এই কোডটিতে পূর্ববর্তী উদাহরণটি পরিবর্তন করতে চান:

var result = ProcessItem((dynamic)testObjects[i], "test" + i, i);

তাহলে ফলাফল অবজেক্টের ধরণ হবে dynamic। এটি কারণ কারণ সংকলক সর্বদা জানেন না যে কোন পদ্ধতিটি ডাকা হবে। আপনি যদি ফাংশন কলের রিটার্নের ধরণটি জানেন তবে আপনার স্পষ্টভাবে এটিকে প্রয়োজনীয় প্রকারে রূপান্তর করা উচিত যাতে বাকী কোডটি স্থিরভাবে টাইপ করা থাকে:

string result = ProcessItem((dynamic)testObjects[i], "test" + i, i);

টাইপটি মেলে না, আপনি রানটাইম ত্রুটি পাবেন।

প্রকৃতপক্ষে, আপনি যদি আগের উদাহরণটিতে ফলাফলের মানটি পেতে চেষ্টা করেন তবে আপনি দ্বিতীয় লুপের পুনরাবৃত্তিতে একটি রানটাইম ত্রুটি পাবেন। এটি কারণ আপনি একটি শূন্য ফাংশনের রিটার্ন মান সংরক্ষণ করার চেষ্টা করেছিলেন।


মারিউস, দ্বারা বিভ্রান্ত "তবে, আপনি যখন আলফা টাইপের যুক্তিটি পাস করবেন তখন রানটাইম ত্রুটি পাবেন কারণ এই বস্তুটি পরিচালনা করতে পারে এমন কোনও পদ্ধতি নেই" "যদি আমি var a = নতুন আলফা () প্রসেসটাইম (ক," পরীক্ষা "+ i) কল করি , i) জেনেরিক প্রসেসটাইম পদ্ধতিটি "জেনারেল প্রসেস আইটেম" আউটপুট করে কার্যকরভাবে এটি পরিচালনা করবে না কেন?
অ্যালেক্স এডেলস্টাইন

@ অ্যালেক্সএডেলস্টেইন আমি আমার উত্তরটি কিছুটা স্পষ্ট করার জন্য সম্পাদনা করেছি। এটি কারণ জেনেরিক ProcessItemপদ্ধতিতে জেনেরিক সীমাবদ্ধতা রয়েছে এবং কেবলমাত্র এমন বস্তু গ্রহণ করে যা IItemইন্টারফেস প্রয়োগ করে । আপনি যখন ফোন করবেন ProcessItem(new Aplha(), "test" , 1);বা ProcessItem((object)(new Aplha()), "test" , 1);আপনি একটি সংকলক ত্রুটি পাবেন তবে dynamicআপনাকে কাস্ট করার সময় সেই চেকটি রানটাইম স্থগিত করুন।
মারিউজ পাভেলস্কি

দুর্দান্ত উত্তর এবং ব্যাখ্যা, আমার জন্য নিখুঁতভাবে কাজ করে। স্বীকৃত উত্তরের চেয়ে আরও ভাল, লেখার চেয়ে খাটো, আরও পারফরম্যান্ট এবং নিরাপদ।
ygoe

17

সি # 4.0 এর সাথে প্রতিচ্ছবিটি প্রয়োজনীয় নয় কারণ ডিএলআর এটি রানটাইম ধরণের সাহায্যে কল করতে পারে। যেহেতু ডিএলআর লাইব্রেরি ব্যবহার করা এক ধরনের বেদনাযুক্ত (আপনার জন্য সি # সংকলক উত্পন্ন কোডের পরিবর্তে), ওপেন সোর্স ফ্রেমওয়ার্ক ডায়নামাইটে (। নেট স্ট্যান্ডার্ড 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}));

13

অ্যাড্রিয়ান গ্যালারির জবাব যোগ করা :

টাইপ তথ্য থেকে জেনেরিক পদ্ধতিতে কল করা তিনটি পদক্ষেপ জড়িত।

টিএলডিআর: একটি টাইপ অবজেক্টের সাথে পরিচিত জেনেরিক পদ্ধতিতে কলিং এর মাধ্যমে সম্পন্ন করা যেতে পারে:

((Action)GenericMethod<object>)
    .Method
    .GetGenericMethodDefinition()
    .MakeGenericMethod(typeof(string))
    .Invoke(this, null);

যেখানে GenericMethod<object>পদ্ধতি কোনো ধরনের মাফিক জেনেরিক সীমাবদ্ধতার ডাকতে নাম এবং হয়।

(ক্রিয়া) যাকে বলা হবে তার পদ্ধতির স্বাক্ষরের সাথে মেলে ( Func<string,string,int>বা Action<bool>)

পদক্ষেপ 1 জেনেরিক পদ্ধতির সংজ্ঞার জন্য মেথডইনফো পাচ্ছে

পদ্ধতি 1: যথাযথ প্রকার বা বাঁধাই পতাকা সহ গেটমেথড () বা গেটমেথডস () ব্যবহার করুন।

MethodInfo method = typeof(Sample).GetMethod("GenericMethod");

পদ্ধতি 2: একটি প্রতিনিধি তৈরি করুন, মেথডইনফো অবজেক্টটি পান এবং তারপরে getGenericMethodDifinition কল করুন

শ্রেণীর অভ্যন্তর থেকে পদ্ধতিগুলি রয়েছে:

MethodInfo method = ((Action)GenericMethod<object>)
    .Method
    .GetGenericMethodDefinition();

MethodInfo method = ((Action)StaticMethod<object>)
    .Method
    .GetGenericMethodDefinition();

ক্লাসের বাইরে থেকে যে পদ্ধতিগুলি রয়েছে:

MethodInfo method = ((Action)(new Sample())
    .GenericMethod<object>)
    .Method
    .GetGenericMethodDefinition();

MethodInfo method = ((Action)Sample.StaticMethod<object>)
    .Method
    .GetGenericMethodDefinition();

সি # তে একটি পদ্ধতির নাম, যেমন "টোস্ট্রিং" বা "জেনেরিকমেথড" আসলে এমন একাধিক পদ্ধতিকে বোঝায় যাতে এক বা একাধিক পদ্ধতি থাকতে পারে। যতক্ষণ না আপনি পদ্ধতির পরামিতিগুলির প্রকারগুলি সরবরাহ করেন, আপনি কোন পদ্ধতিটি উল্লেখ করছেন তা জানা যায়নি।

((Action)GenericMethod<object>)একটি নির্দিষ্ট পদ্ধতির জন্য প্রতিনিধি বোঝায়। ((Func<string, int>)GenericMethod<object>) জেনেরিকমেথোডের আলাদা ওভারলোড বোঝায়

পদ্ধতি 3: একটি মেথড কল এক্সপ্রেশন সম্বলিত একটি ল্যাম্বডা এক্সপ্রেশন তৈরি করুন, মেথডইনফো অবজেক্টটি পান এবং তারপরে getGenericMethodD Definition

MethodInfo method = ((MethodCallExpression)((Expression<Action<Sample>>)(
    (Sample v) => v.GenericMethod<object>()
    )).Body).Method.GetGenericMethodDefinition();

এটি ভেঙে যায়

একটি ল্যাম্বডা এক্সপ্রেশন তৈরি করুন যেখানে শরীরটি আপনার পছন্দসই পদ্ধতিতে কল call

Expression<Action<Sample>> expr = (Sample v) => v.GenericMethod<object>();

দেহটি বের করুন এবং মেথডোক্যালএক্সপ্রেসনে কাস্ট করুন

MethodCallExpression methodCallExpr = (MethodCallExpression)expr.Body;

পদ্ধতিটি থেকে জেনেরিক পদ্ধতির সংজ্ঞা পান

MethodInfo methodA = methodCallExpr.Method.GetGenericMethodDefinition();

পদক্ষেপ 2 যথাযথ ধরণের (গুলি) দিয়ে জেনেরিক পদ্ধতি তৈরি করতে মেকগেনেরিকমেথোডকে কল করছে।

MethodInfo generic = method.MakeGenericMethod(myType);

পদক্ষেপ 3 যথাযথ যুক্তি দিয়ে পদ্ধতিটি আহ্বান করছে।

generic.Invoke(this, null);

8

কেউই " ক্লাসিক প্রতিবিম্ব " সমাধানটি সরবরাহ করেন নি, তাই এখানে একটি সম্পূর্ণ কোড উদাহরণ রয়েছে:

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)

এবং এটি একটি আইডিকেয়ার উদাহরণ তৈরি করে এবং ফেরত দেয়, যার কী এবং মানগুলি কল keyTypeএবং তার ধরণের নির্দিষ্টগুলি নির্দিষ্ট valueType

এটিকে ইনস্ট্যান্টিয়েট করার জন্য এবং কীভাবে এই পদ্ধতিটি কল করতে হবে তার একটি সম্পূর্ণ উদাহরণ এখানে রয়েছে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

2

এটি গ্রাক্সের উত্তরের ভিত্তিতে আমার 2 সেন্ট , তবে জেনেরিক পদ্ধতির জন্য প্রয়োজনীয় দুটি পরামিতি।

ধরুন আপনার পদ্ধতিটি কোনও সহায়ক শ্রেণিতে নিম্নলিখিত হিসাবে সংজ্ঞায়িত হয়েছে:

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

আমার ক্ষেত্রে, ইউ টাইপ টি সর্বদা পর্যবেক্ষণযোগ্য সংগ্রহ স্টোর অবজেক্ট object

যেহেতু আমার প্রকারগুলি পূর্বনির্ধারিত রয়েছে, আমি প্রথমে "ডামি" অবজেক্ট তৈরি করব যা পর্যবেক্ষণযোগ্য সংগ্রহ (ইউ) এবং এটিতে (টি) সঞ্চিত বস্তুর প্রতিনিধিত্ব করে এবং মেক কল করার সময় এগুলি তাদের ধরণের পেতে নীচে ব্যবহার করা হবে

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

তারপরে আপনার জেনেরিক ফাংশনটি সন্ধানের জন্য গেটমেথোদকে কল করুন:

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

এখনও অবধি, উপরোক্ত কলটি উপরে বর্ণিত মতটি তুলনায় অনেকটা অভিন্ন কিন্তু যখন আপনাকে একাধিক পরামিতিগুলি পাস করতে হবে তখন একটি ছোট পার্থক্য সহ।

আপনাকে MakeGenericMethod ফাংশনে একটি প্রকার [] অ্যারে পাস করতে হবে যা উপরে তৈরি করা "ডামি" অবজেক্টের ধরণের রয়েছে:

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

এটি হয়ে গেলে, উপরোক্ত অনুসারে আপনাকে ইনভোক পদ্ধতিটি কল করতে হবে।

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

এবং তুমি করে ফেলেছ. একটি কবজ কাজ করে!

হালনাগাদ:

@ বিভান যেমন হাইলাইট করেছেন, MakeGenericMethod ফাংশনটি প্যারাম হিসাবে গ্রহণ করার সময় কল করার সময় আমার কোনও অ্যারে তৈরি করার দরকার নেই এবং প্রকারগুলি পাওয়ার জন্য আমার কোনও অবজেক্ট তৈরি করার দরকার নেই কারণ আমি কেবল এই ফাংশনে সরাসরি প্রকারগুলি পার করতে পারি। আমার ক্ষেত্রে, যেহেতু আমার কাছে অন্য শ্রেণিতে পূর্বনির্ধারিত প্রকারগুলি রয়েছে, তাই আমি কেবল আমার কোডটি এতে পরিবর্তন করেছি:

object myCollection = null;

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

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

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

মাইক্লাসআইএনফোতে 2 ধরণের প্রকারের বৈশিষ্ট্য রয়েছে Typeযা আমি কনস্ট্রাক্টরের কাছে দেওয়া এনাম মানের উপর ভিত্তি করে রান টাইমে সেট করেছিলাম এবং আমাকে প্রাসঙ্গিক প্রকারগুলি প্রদান করব যা আমি পরে মেকজেনেরিকমেথোডে ব্যবহার করি।

এই @ বিভেনটি হাইলাইট করার জন্য আবার ধন্যবাদ।


আর্গুমেন্ট MakeGenericMethod()আছে প্যারাম keyword যাতে আপনি একটি অ্যারের তৈরি করতে হবে না; বা টাইপগুলি পেতে আপনার উদাহরণ তৈরি করার দরকার নেই - methodInfo.MakeGenericMethod(typeof(TCollection), typeof(TObject))যথেষ্ট হবে।
বেভান

0

ছদ্মবেশ ধারণার উত্তরে অনুপ্রাণিত - ধরে নেওয়া যাক আপনার যেমন দুটি (বা তার বেশি) ক্লাস রয়েছে

public class Bar { }
public class Square { }

এবং আপনি পদ্ধতিটি Foo<T>সাথে কল করতে চান Barএবং Square, যা হিসাবে ঘোষণা করা হয়

public class myClass
{
    public void Foo<T>(T item)
    {
        Console.WriteLine(typeof(T).Name);
    }
}

তারপরে আপনি কোনও এক্সটেনশন পদ্ধতিটি প্রয়োগ করতে পারেন:

public static class Extension
{
    public static void InvokeFoo<T>(this T t)
    {
        var fooMethod = typeof(myClass).GetMethod("Foo");
        var tType = typeof(T);
        var fooTMethod = fooMethod.MakeGenericMethod(new[] { tType });
        fooTMethod.Invoke(new myClass(), new object[] { t });
    }
}

এটির সাহায্যে আপনি সহজেই এইভাবে প্রার্থনা করতে Fooপারেন:

var objSquare = new Square();
objSquare.InvokeFoo();

var objBar = new Bar();
objBar.InvokeFoo();

যা প্রতিটি শ্রেণীর জন্য কাজ করে। এই ক্ষেত্রে, এটি আউটপুট হবে:

স্কয়ার
বার

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.