ডায়নামিক ভেরিয়েবল থাকার ফলে কর্মক্ষমতা কীভাবে প্রভাবিত হয়?


127

dynamicসি # তে পারফরম্যান্স নিয়ে আমার একটি প্রশ্ন আছে । আমি পড়েছি dynamicসংকলকটি আবার চালিত করে, তবে এটি কী করে?

dynamicপ্যারামিটার হিসাবে ব্যবহৃত ভেরিয়েবলের সাথে পুরো গতিপথটি পুনরায় কম্পাইল করতে হবে বা গতিশীল আচরণ / প্রসঙ্গের সাথে কেবল সেই লাইনগুলিই তৈরি করতে হবে?

আমি লক্ষ্য করেছি যে dynamicভেরিয়েবলগুলি ব্যবহার করে লুপের জন্য 2 মাপের দৈর্ঘ্যের দ্বারা একটি সাধারণ গতি কমিয়ে দিতে পারে।

কোডটি আমি এটি দিয়ে খেলেছি:

internal class Sum2
{
    public int intSum;
}

internal class Sum
{
    public dynamic DynSum;
    public int intSum;
}

class Program
{
    private const int ITERATIONS = 1000000;

    static void Main(string[] args)
    {
        var stopwatch = new Stopwatch();
        dynamic param = new Object();
        DynamicSum(stopwatch);
        SumInt(stopwatch);
        SumInt(stopwatch, param);
        Sum(stopwatch);

        DynamicSum(stopwatch);
        SumInt(stopwatch);
        SumInt(stopwatch, param);
        Sum(stopwatch);

        Console.ReadKey();
    }

    private static void Sum(Stopwatch stopwatch)
    {
        var sum = 0;
        stopwatch.Reset();
        stopwatch.Start();
        for (int i = 0; i < ITERATIONS; i++)
        {
            sum += i;
        }
        stopwatch.Stop();

        Console.WriteLine(string.Format("Elapsed {0}", stopwatch.ElapsedMilliseconds));
    }

    private static void SumInt(Stopwatch stopwatch)
    {
        var sum = new Sum();
        stopwatch.Reset();
        stopwatch.Start();
        for (int i = 0; i < ITERATIONS; i++)
        {
            sum.intSum += i;
        }
        stopwatch.Stop();

        Console.WriteLine(string.Format("Class Sum int Elapsed {0}", stopwatch.ElapsedMilliseconds));
    }

    private static void SumInt(Stopwatch stopwatch, dynamic param)
    {
        var sum = new Sum2();
        stopwatch.Reset();
        stopwatch.Start();
        for (int i = 0; i < ITERATIONS; i++)
        {
            sum.intSum += i;
        }
        stopwatch.Stop();

        Console.WriteLine(string.Format("Class Sum int Elapsed {0} {1}", stopwatch.ElapsedMilliseconds, param.GetType()));
    }

    private static void DynamicSum(Stopwatch stopwatch)
    {
        var sum = new Sum();
        stopwatch.Reset();
        stopwatch.Start();
        for (int i = 0; i < ITERATIONS; i++)
        {
            sum.DynSum += i;
        }
        stopwatch.Stop();

        Console.WriteLine(String.Format("Dynamic Sum Elapsed {0}", stopwatch.ElapsedMilliseconds));
    }

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

উত্তর:


232

আমি ডায়নামিক পড়েছি কম্পাইলারটি আবার চালাচ্ছে, তবে এটি কী করে। প্যারামিটার হিসাবে ব্যবহৃত ডায়নামিকের সাথে গতিশীল আচরণ / প্রসঙ্গ (?) সহ সেই লাইনগুলি কী পুরো পদ্ধতিটি পুনরায় কম্পাইল করতে হবে?

এখানে চুক্তি।

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

class C
{
    void M()
    {
        dynamic d1 = whatever;
        dynamic d2 = d1.Foo();

তারপরে সংকলকটি নৈতিকভাবে এর মতো কোড তৈরি করবে। (আসল কোডটি খানিকটা জটিল; এটি উপস্থাপনের উদ্দেশ্যে সহজ করা হয়েছে))

class C
{
    static DynamicCallSite FooCallSite;
    void M()
    {
        object d1 = whatever;
        object d2;
        if (FooCallSite == null) FooCallSite = new DynamicCallSite();
        d2 = FooCallSite.DoInvocation("Foo", d1);

এখন পর্যন্ত কিভাবে এটি কাজ করে দেখুন? আমরা একবার কল সাইটটি উত্পন্ন করি , আপনি এম কে যতবার কল করেন না কেন আপনি একবার সাইট তৈরি করার পরে কল সাইটটি চিরকাল বেঁচে থাকে। কল সাইটটি এমন একটি অবজেক্ট যা প্রতিনিধিত্ব করে "এখানে ফু-তে ডায়নামিক কল হতে চলেছে" represents

ঠিক আছে, সুতরাং এখন আপনি কল সাইট পেয়েছেন, অনুরোধ কাজ করে কিভাবে?

কল সাইটটি ডায়নামিক ল্যাঙ্গুয়েজ রানটাইমের অংশ। ডিএলআর বলেছে "হুঁ, কেউ এখানে কোনও পদক্ষেপের ডায়নামিক অনুরোধ করার চেষ্টা করছেন এই বিষয়টিতে। আমি কি সে সম্পর্কে কিছু জানি? না। তবে আমি আরও ভাল করে খুঁজে বের করতে পারি।"

ডিএলআর তখন ডি 1 এ বস্তুকে জিজ্ঞাসাবাদ করে এটি কোনও বিশেষ কি না তা দেখুন। হতে পারে এটি কোনও উত্তরাধিকারী COM অবজেক্ট, বা একটি আয়রন পাইথন অবজেক্ট, বা একটি আয়রন রুবি অবজেক্ট, বা একটি আই ডোম অবজেক্ট। যদি এটিগুলির মধ্যে কোনও না হয় তবে এটি অবশ্যই একটি সাধারণ সি # অবজেক্ট।

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

মেটাডেটা বিশ্লেষক ডি 1-তে বস্তুর ধরণ নির্ধারণ করতে প্রতিবিম্ব ব্যবহার করে এবং তারপরে সিন্থেটিক বিশ্লেষকের কাছে এমনটি জিজ্ঞাসা করে যে এই জাতীয় বস্তুটি মেথডু ফোতে যখন অনুরোধ করা হয় তখন কী ঘটে। ওভারলোড রেজোলিউশন বিশ্লেষক এটিকে দেখায় এবং তারপরে একটি এক্সপ্রেশন ট্রি তৈরি করে - ঠিক যেমন আপনি কোনও এক্সপ্রেশন ট্রি ল্যাম্বডায় ফু কে ডেকেছিলেন - যা সেই কলটিকে উপস্থাপন করে।

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

ডিএলআর তখন এই প্রতিনিধিটিকে কল সাইট অবজেক্টের সাথে সম্পর্কিত ক্যাশে ক্যাশে রাখে।

তারপরে এটি প্রতিনিধিকে অনুরোধ জানায়, এবং ফু কল ঘটে।

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

এটি গতিশীল জড়িত প্রতিটি অভিব্যক্তির জন্য ঘটে । সুতরাং উদাহরণস্বরূপ যদি আপনার কাছে থাকে:

int x = d1.Foo() + d2;

তারপরে তিনটি গতিশীল কল সাইট রয়েছে। ফু-তে ডায়নামিক কলের জন্য একটি, গতিশীল সংযোজনের জন্য একটি এবং ডায়নামিক থেকে ইনটিতে গতিশীল রূপান্তরের জন্য একটি। প্রত্যেকের নিজস্ব রানটাইম বিশ্লেষণ এবং বিশ্লেষণ ফলাফলের নিজস্ব ক্যাশে রয়েছে।

ধারণা তৈরী কর?


কৌতূহলের বাইরে, পার্সার / লেক্সার ছাড়াই বিশেষ সংকলক সংস্করণটি স্ট্যান্ডার্ড সিএসসি.এক্সে একটি বিশেষ পতাকা পাঠিয়ে অনুরোধ করা হচ্ছে?
রোমান রয়টার

@ এরিক, আপনি কি আমার আগের কোনও ব্লগ পোস্টে আমাকে নির্দেশ করতে আপনাকে ঝামেলা করতে পারেন যেখানে আপনি সংক্ষিপ্ত, অভ্যন্তরীণ ইত্যাদির অন্তর্নিহিত রূপান্তর সম্পর্কে কথা বলছেন? আমি আপনাকে এখানে স্মরণ করিয়ে দিয়েছি যে কীভাবে / কেন কনভার্টের সাথে ডায়নামিক ব্যবহার করা হয় o টক্সএক্সএক্সএক্স সংকলকটিকে আগুন জ্বালিয়ে দেয়। আমি নিশ্চিত আমি বিশদটি কসাই করছি তবে আশা করি আপনি কী জানেন আমি কী সম্পর্কে কথা বলছি।
অ্যাডাম রাকিস

4
@ রোমান: নং সিএসসি.এক্স.এ সি ++ তে লেখা আছে এবং আমাদের এমন কিছু প্রয়োজন যা আমরা সহজেই সি # থেকে কল করতে পারি। এছাড়াও, মূললাইন সংকলকটির নিজস্ব ধরণের অবজেক্ট রয়েছে তবে প্রতিবিম্বের ধরণের অবজেক্টগুলি ব্যবহার করতে আমাদের সক্ষম হওয়া দরকার। আমরা csc.exe সংকলক থেকে সি ++ কোডের প্রাসঙ্গিক অংশগুলি সরিয়ে নিয়েছি এবং সেগুলি লাইন বাই লাইন সি # তে অনুবাদ করেছি এবং তারপরে ডিএলআর কল করার জন্য একটি লাইব্রেরি তৈরি করেছি।
এরিক লিপার্ট

9
@ এরিক, "আমরা সিসিএস.এক্স.কম সংকলক থেকে সি ++ কোডের প্রাসঙ্গিক অংশগুলি বের করেছিলাম এবং তাদের লাইন বাই লাইনে সি # তে অনুবাদ করেছি" তখনই লোকেরা ভেবেছিল যে
রোজলিন

5
@ শুগিকুক: একটি সংকলক হিসাবে কোনও পরিষেবা থাকার ধারণাটি বেশ কিছুদিন ধরেই ঘুরছিল, তবে আসলে কোড বিশ্লেষণের জন্য একটি রানটাইম পরিষেবা দরকার ছিল হ্যাঁ, এই প্রকল্পের পক্ষে একটি বড় উত্সাহ ছিল।
এরিক লিপার্ট

107

আপডেট: প্রাক-কম্পাইল এবং অলস-সংকলিত বেঞ্চমার্কগুলি যুক্ত করা হয়েছে

আপডেট 2: সক্রিয়, আমি ভুল। সম্পূর্ণ এবং সঠিক উত্তরের জন্য এরিক লিপার্টের পোস্টটি দেখুন। আমি এখানে বেনমার্ক সংখ্যাগুলির জন্য রেখে যাচ্ছি

* আপডেট 3: এই প্রশ্নের মার্ক গ্রেভেলের উত্তরের ভিত্তিতে আইএল-নির্গত এবং অলস আইএল-নির্গত মানদণ্ড যুক্ত হয়েছে ।

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

পারফরম্যান্স সম্পর্কে, dynamicঅন্তর্নিহিতভাবে কিছু ওভারহেড পরিচয় করিয়ে দেয় তবে আপনি যতটা ভাবেন ততটা প্রায় নয়। উদাহরণস্বরূপ, আমি সবেমাত্র একটি মানদণ্ড চালিয়েছি যা দেখতে এরকম দেখাচ্ছে:

void Main()
{
    Foo foo = new Foo();
    var args = new object[0];
    var method = typeof(Foo).GetMethod("DoSomething");
    dynamic dfoo = foo;
    var precompiled = 
        Expression.Lambda<Action>(
            Expression.Call(Expression.Constant(foo), method))
        .Compile();
    var lazyCompiled = new Lazy<Action>(() =>
        Expression.Lambda<Action>(
            Expression.Call(Expression.Constant(foo), method))
        .Compile(), false);
    var wrapped = Wrap(method);
    var lazyWrapped = new Lazy<Func<object, object[], object>>(() => Wrap(method), false);
    var actions = new[]
    {
        new TimedAction("Direct", () => 
        {
            foo.DoSomething();
        }),
        new TimedAction("Dynamic", () => 
        {
            dfoo.DoSomething();
        }),
        new TimedAction("Reflection", () => 
        {
            method.Invoke(foo, args);
        }),
        new TimedAction("Precompiled", () => 
        {
            precompiled();
        }),
        new TimedAction("LazyCompiled", () => 
        {
            lazyCompiled.Value();
        }),
        new TimedAction("ILEmitted", () => 
        {
            wrapped(foo, null);
        }),
        new TimedAction("LazyILEmitted", () => 
        {
            lazyWrapped.Value(foo, null);
        }),
    };
    TimeActions(1000000, actions);
}

class Foo{
    public void DoSomething(){}
}

static Func<object, object[], object> Wrap(MethodInfo method)
{
    var dm = new DynamicMethod(method.Name, typeof(object), new Type[] {
        typeof(object), typeof(object[])
    }, method.DeclaringType, true);
    var il = dm.GetILGenerator();

    if (!method.IsStatic)
    {
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Unbox_Any, method.DeclaringType);
    }
    var parameters = method.GetParameters();
    for (int i = 0; i < parameters.Length; i++)
    {
        il.Emit(OpCodes.Ldarg_1);
        il.Emit(OpCodes.Ldc_I4, i);
        il.Emit(OpCodes.Ldelem_Ref);
        il.Emit(OpCodes.Unbox_Any, parameters[i].ParameterType);
    }
    il.EmitCall(method.IsStatic || method.DeclaringType.IsValueType ?
        OpCodes.Call : OpCodes.Callvirt, method, null);
    if (method.ReturnType == null || method.ReturnType == typeof(void))
    {
        il.Emit(OpCodes.Ldnull);
    }
    else if (method.ReturnType.IsValueType)
    {
        il.Emit(OpCodes.Box, method.ReturnType);
    }
    il.Emit(OpCodes.Ret);
    return (Func<object, object[], object>)dm.CreateDelegate(typeof(Func<object, object[], object>));
}

আপনি কোড থেকে দেখতে পাচ্ছেন, আমি একটি সাধারণ নো-অপশন পদ্ধতিটি সাতটি ভিন্ন উপায়ে চেষ্টা করার চেষ্টা করব:

  1. সরাসরি পদ্ধতি কল
  2. ব্যবহার dynamic
  3. প্রতিবিম্ব দ্বারা
  4. Actionরানটাইমে প্রাক-কম্পাইল হওয়া একটি ব্যবহার করে (ফলস্বরূপ সংকলনের সময় বাদ দিয়ে)।
  5. একটি Actionথ্রেড-নিরাপদ অলস ভেরিয়েবল (এটি সংকলনের সময় সহ) ব্যবহার করে প্রথমবারের জন্য প্রয়োজনীয় সংকলন করা একটি ব্যবহার করে
  6. পরীক্ষার আগে তৈরি হয়ে যায় এমন একটি গতিময়ভাবে তৈরি পদ্ধতি ব্যবহার
  7. পরীক্ষার সময় অলসভাবে তাত্ক্ষণিকভাবে গতিশীলভাবে উত্পন্ন পদ্ধতি ব্যবহার করা method

প্রত্যেকে একটি সাধারণ লুপে 1 মিলিয়ন বার কল করে। সময় ফলাফল এখানে:

সরাসরি: 3.4248ms
ডায়নামিক: 45.0728ms
প্রতিবিম্ব
: 888.4011ms
প্রাকম্পম্পিত: 21.9166ms
অলস কম্পাইল: 30.2045
মিমি ইলিডিত: 8.4918ms অলস: 14.3483ms

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

পারফরম্যান্স dynamicঅকারণে ব্যবহার না করার অনেকগুলি ভাল কারণগুলির মধ্যে একটি , তবে আপনি যখন সত্যিকারের dynamicডেটা নিয়ে কাজ করছেন তখন এটি এমন সুবিধাগুলি সরবরাহ করতে পারে যা অসুবিধাগুলি ছাড়িয়ে যায়।

আপডেট 4

জনবটের মন্তব্যের ভিত্তিতে, আমি প্রতিবিম্ব অঞ্চলটি চারটি পৃথক পরীক্ষায় ভেঙে ফেলেছি:

    new TimedAction("Reflection, find method", () => 
    {
        typeof(Foo).GetMethod("DoSomething").Invoke(foo, args);
    }),
    new TimedAction("Reflection, predetermined method", () => 
    {
        method.Invoke(foo, args);
    }),
    new TimedAction("Reflection, create a delegate", () => 
    {
        ((Action)method.CreateDelegate(typeof(Action), foo)).Invoke();
    }),
    new TimedAction("Reflection, cached delegate", () => 
    {
        methodDelegate.Invoke();
    }),

... এবং এখানে মাপদণ্ডের ফলাফল:

এখানে চিত্র বর্ণনা লিখুন

সুতরাং আপনি যদি একটি নির্দিষ্ট পদ্ধতিটি নির্ধারণ করতে পারেন যা আপনাকে প্রচুর কল করতে হবে, সেই পদ্ধতিটির উল্লেখ করে একটি ক্যাশেড প্রতিনিধিকে আহ্বান করা পদ্ধতিটির কলিংয়ের মতোই দ্রুত fast তবে, আপনি যদি কোন পদ্ধতিটি কল করতে চান ঠিক তেমন পদ্ধতি নির্ধারণের প্রয়োজন হয় তবে এর জন্য একটি প্রতিনিধি তৈরি করা খুব ব্যয়বহুল।


2
যেমন একটি বিস্তারিত প্রতিক্রিয়া, ধন্যবাদ! আমি আসল নম্বরগুলি সম্পর্কেও ভাবছিলাম।
সের্গেই সিরিটকিন

4
ঠিক আছে, ডায়নামিক কোডটি মেটাডেটা আমদানিকারক, সংমিশ্রণ বিশ্লেষক এবং সংকলকটির এক্সপ্রেশন ট্রি ইমিটার শুরু করে এবং তারপরে আউটপুটটিতে একটি এক্সপ্রেশন-টু-ইল সংকলক চালায়, তাই আমি মনে করি যে এটি শুরু হয় বলা ঠিক হবে রানটাইম এ সংকলক আপ। কেবল কারণ এটি লেক্সারটি চালায় না এবং পার্সারটিকে খুব প্রাসঙ্গিক বলে মনে হচ্ছে।
এরিক লিপার্ট

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

1
এরিকের পরামর্শ অনুসারে কিছু বোকা। কোন লাইন মন্তব্য করা হয়েছে তা অদলবদল করে পরীক্ষা করুন। 8964ms বনাম 814ms, dynamicঅবশ্যই হারাতে:public class ONE<T>{public object i { get; set; }public ONE(){i = typeof(T).ToString();}public object make(int ix){ if (ix == 0) return i;ONE<ONE<T>> x = new ONE<ONE<T>>();/*dynamic x = new ONE<ONE<T>>();*/return x.make(ix - 1);}}ONE<END> x = new ONE<END>();string lucky;Stopwatch sw = new Stopwatch();sw.Start();lucky = (string)x.make(500);sw.Stop();Trace.WriteLine(sw.ElapsedMilliseconds);Trace.WriteLine(lucky);
ব্রায়ান

1
প্রতিবিম্বিত হতে ন্যায়সঙ্গত হন এবং পদ্ধতি তথ্য থেকে একটি প্রতিনিধি তৈরি করুন:var methodDelegate = (Action)method.CreateDelegate(typeof(Action), foo);
জনবট
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.