প্রতিবিম্ব: পরামিতিগুলির সাহায্যে পদ্ধতি কীভাবে উপস্থাপন করা যায়


197

আমি প্যারামিটারগুলির সাথে প্রতিবিম্বের মাধ্যমে কোনও পদ্ধতিটি আহ্বানের চেষ্টা করছি এবং আমি পেয়েছি:

লক্ষ্য লক্ষ্য প্রকারের সাথে মেলে না

আমি যদি প্যারামিটার ছাড়াই কোনও পদ্ধতিতে প্রার্থনা করি তবে এটি ভাল কাজ করে। নিম্নলিখিত কোডের উপর ভিত্তি করে যদি আমি পদ্ধতিটি কল করি তবে Test("TestNoParameters")এটি দুর্দান্ত কাজ করে। তবে আমি যদি ফোন করি তবে আমি Test("Run")ব্যতিক্রম পাই। আমার কোডে কিছু ভুল আছে?

আমার প্রাথমিক উদ্দেশ্যটি ছিল একটি সামগ্রীর অ্যারে পাস করানো public void Run(object[] options)but উদাহরণস্বরূপ, তবে এটি কার্যকর হয়নি এবং আমি সহজ কিছু চেষ্টা করেছি string সাফল্য ছাড়াই স্ট্রিং।

// Assembly1.dll
namespace TestAssembly
{
    public class Main
    {
        public void Run(string parameters)
        { 
            // Do something... 
        }
        public void TestNoParameters()
        {
            // Do something... 
        }
    }
}

// Executing Assembly.exe
public class TestReflection
{
    public void Test(string methodName)
    {
        Assembly assembly = Assembly.LoadFile("...Assembly1.dll");
        Type type = assembly.GetType("TestAssembly.Main");

        if (type != null)
        {
            MethodInfo methodInfo = type.GetMethod(methodName);

            if (methodInfo != null)
            {
                object result = null;
                ParameterInfo[] parameters = methodInfo.GetParameters();
                object classInstance = Activator.CreateInstance(type, null);

                if (parameters.Length == 0)
                {
                    // This works fine
                    result = methodInfo.Invoke(classInstance, null);
                }
                else
                {
                    object[] parametersArray = new object[] { "Hello" };

                    // The invoke does NOT work;
                    // it throws "Object does not match target type"             
                    result = methodInfo.Invoke(methodInfo, parametersArray);
                }
            }
        }
    }
}

4
সঠিক লাইনটি অবজেক্ট হবে [] পরামিতি অ্যারে = নতুন অবজেক্ট [] object নতুন অবজেক্ট [] {"হ্যালো"}};
নিক কোভালস্কি

উত্তর:


236

নাল প্যারামিটার অ্যারের সাথে কল করার মতো "মেথডআইনফো" কে "ক্লাসি ইনস্ট্যান্স" এ পরিবর্তন করুন।

  result = methodInfo.Invoke(classInstance, parametersArray);

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

4
যদি প্যারামিটারগুলি একাধিক ধরণের হয় তবে অ্যারের কেমন হওয়া উচিত? বস্তুর একটি অ্যারে ??
রাদু ভ্লাদ

হ্যাঁ, এটা একটি বস্তু [] যদি আর্গুমেন্ট একাধিক ধরনের হয় হওয়া উচিত
মার্টিন জোহানসন

29

আপনার ঠিক এখনই একটি বাগ আছে

result = methodInfo.Invoke(methodInfo, parametersArray);

এটা করা উচিত

result = methodInfo.Invoke(classInstance, parametersArray);

24

একটি মৌলিক ভুল এখানে:

result = methodInfo.Invoke(methodInfo, parametersArray); 

আপনি একটি উদাহরণে পদ্ধতিতে প্রার্থনা করছেন MethodInfo। আপনি যে ধরণের অবজেক্টটি চালু করতে চান তার একটি উদাহরণ আপনাকে পাস করতে হবে।

result = methodInfo.Invoke(classInstance, parametersArray);

11

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

এটি আমার ক্লাসইনস্ট্যান্সটি তৈরি করা দরকার কারণ এটি কোনও দূরবর্তী সমাবেশে অবস্থিত।

// sample of my CreateInstance call with an explicit assembly reference
object classInstance = Activator.CreateInstance(assemblyName, type.FullName); 

তবে উপরে বর্ণিত উত্তর সহ, আপনি এখনও একই ত্রুটি পাবেন। কীভাবে যাবেন তা এখানে:

// first, create a handle instead of the actual object
ObjectHandle classInstanceHandle = Activator.CreateInstance(assemblyName, type.FullName);
// unwrap the real slim-shady
object classInstance = classInstanceHandle.Unwrap(); 
// re-map the type to that of the object we retrieved
type = classInstace.GetType(); 

তারপরে এখানে উল্লিখিত অন্যান্য ব্যবহারকারীদের মতো করুন।


5

আমি এটি এর মতো ব্যবহার করব, এর পথটি আরও ছোট এবং এটি কোনও সমস্যা দেবে না

        dynamic result = null;
        if (methodInfo != null)
        {
            ParameterInfo[] parameters = methodInfo.GetParameters();
            object classInstance = Activator.CreateInstance(type, null);
            result = methodInfo.Invoke(classInstance, parameters.Length == 0 ? null : parametersArray);
        }

3
 Assembly assembly = Assembly.LoadFile(@"....bin\Debug\TestCases.dll");
       //get all types
        var testTypes = from t in assembly.GetTypes()
                        let attributes = t.GetCustomAttributes(typeof(NUnit.Framework.TestFixtureAttribute), true)
                        where attributes != null && attributes.Length > 0
                        orderby t.Name
                        select t;

        foreach (var type in testTypes)
        {
            //get test method in types.
            var testMethods = from m in type.GetMethods()
                              let attributes = m.GetCustomAttributes(typeof(NUnit.Framework.TestAttribute), true)
                              where attributes != null && attributes.Length > 0
                              orderby m.Name
                              select m;

            foreach (var method in testMethods)
            {
                MethodInfo methodInfo = type.GetMethod(method.Name);

                if (methodInfo != null)
                {
                    object result = null;
                    ParameterInfo[] parameters = methodInfo.GetParameters();
                    object classInstance = Activator.CreateInstance(type, null);

                    if (parameters.Length == 0)
                    {
                        // This works fine
                        result = methodInfo.Invoke(classInstance, null);
                    }
                    else
                    {
                        object[] parametersArray = new object[] { "Hello" };

                        // The invoke does NOT work;
                        // it throws "Object does not match target type"             
                        result = methodInfo.Invoke(classInstance, parametersArray);
                    }
                }

            }
        }

3

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

আমি বিশ্বাস করি যদি আপনি Mainনীচের মত কিছু পদ্ধতিতে বা এমনকি আপনার প্রশ্নের মতো একক প্যারামিটার stringদিয়ে কল objectকরছেন তবে এটি কাজ করার জন্য আপনাকে কেবলমাত্র প্যারামিটারের ধরনটি পরিবর্তন করতে হবে । আমার নিচের মত ক্লাস আছে

//Assembly.dll
namespace TestAssembly{
    public class Main{

        public void Hello()
        { 
            var name = Console.ReadLine();
            Console.WriteLine("Hello() called");
            Console.WriteLine("Hello" + name + " at " + DateTime.Now);
        }

        public void Run(string parameters)
        { 
            Console.WriteLine("Run() called");
            Console.Write("You typed:"  + parameters);
        }

        public string TestNoParameters()
        {
            Console.WriteLine("TestNoParameters() called");
            return ("TestNoParameters() called");
        }

        public void Execute(object[] parameters)
        { 
            Console.WriteLine("Execute() called");
           Console.WriteLine("Number of parameters received: "  + parameters.Length);

           for(int i=0;i<parameters.Length;i++){
               Console.WriteLine(parameters[i]);
           }
        }

    }
}

তারপরে আপনাকে অনুরোধ করার সময় নীচের মতো কোনও বস্তুর অ্যারেতে প্যারামিটারআরেটি পাস করতে হবে। নিম্নলিখিত কাজটি আপনার কাজ করা দরকার

private void ExecuteWithReflection(string methodName,object parameterObject = null)
{
    Assembly assembly = Assembly.LoadFile("Assembly.dll");
    Type typeInstance = assembly.GetType("TestAssembly.Main");

    if (typeInstance != null)
    {
        MethodInfo methodInfo = typeInstance.GetMethod(methodName);
        ParameterInfo[] parameterInfo = methodInfo.GetParameters();
        object classInstance = Activator.CreateInstance(typeInstance, null);

        if (parameterInfo.Length == 0)
        {
            // there is no parameter we can call with 'null'
            var result = methodInfo.Invoke(classInstance, null);
        }
        else
        {
            var result = methodInfo.Invoke(classInstance,new object[] { parameterObject } );
        }
    }
}

এই পদ্ধতিটি পদ্ধতিটি শুরু করা সহজ করে তোলে, এটি নিম্নলিখিত হিসাবে বলা যেতে পারে

ExecuteWithReflection("Hello");
ExecuteWithReflection("Run","Vinod");
ExecuteWithReflection("TestNoParameters");
ExecuteWithReflection("Execute",new object[]{"Vinod","Srivastav"});

1

আমি প্রতিবিম্বের মাধ্যমে ভারিত গড়কে প্রার্থনা করছি। এবং একাধিক প্যারামিটার সহ পদ্ধতি ব্যবহার করেছিল।

Class cls = Class.forName(propFile.getProperty(formulaTyp));// reading class name from file

Object weightedobj = cls.newInstance(); // invoke empty constructor

Class<?>[] paramTypes = { String.class, BigDecimal[].class, BigDecimal[].class }; // 3 parameter having first is method name and other two are values and their weight
Method printDogMethod = weightedobj.getClass().getMethod("applyFormula", paramTypes); // created the object 
return BigDecimal.valueOf((Double) printDogMethod.invoke(weightedobj, formulaTyp, decimalnumber, weight)); calling the method

0
string result = this.GetType().GetMethod("Print").Invoke(this, new object[]{"firstParam", 157, "third_Parammmm" } );

যদি এটি বাহ্যিক না হয়। dll (এর পরিবর্তে this.GetType(), আপনি ব্যবহার করতে পারেন typeof(YourClass))।

পিএস এই উত্তরটি পোস্ট করছে কারণ অনেক দর্শক এই উত্তরের জন্য এখানে প্রবেশ করে।

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