PBrenek
PBrenek

Reputation: 571

C#: Using Reflection to dynamically call an explicitly implemented method

I am trying to dynamically call an explicitly implemented method using reflection. My method is as follows:

public static double GetValueOfAverage(string typeName, string methodName, object[] arguments){
    // ... get the Type for the class
    Type calledType = Type.GetType(typeName, true);

    double avg  = (double)calledType.InvokeMember(
        methodName,
        BindingFlags.InvokeMethod | BindingFlags.Public | 
        BindingFlags.Static | BindingFlags.NonPublic,
        null,
        null,
        new object[] { arguments }
    );

    // Return the value returned by the called method.
    return avg;
}

I call this method as follows:

// ... calculate and set the slow moving average value
var slowArguments = new object[] { SlowPeriod, CurrentBar, Input, slowAverage};
movingAvg = GetValueOfAverage("MovingAverage", slowAvgMethod, slowArguments);

And my moving average class is implemented as follows:

public static class MovingAverage{
    public static double _EMA(int Period, int CurrentBar, DataSeries Input, DataSeries average){
        double avg = CurrentBar == 0 ? Input[0] : Input[0] * (2.0 / (1 + Period)) + (1 - (2.0 / (1 + Period))) * average[1];
        return avg;
    }
}

I get the following error:

Could not load type 'MovingAverage' from assembly 'f9e1d550bd4d44ddbdc78abaffa07d31, Version=7.0.1000.22, Culture=neutral, PublicKeyToken=null'.

I have tried adding the namespace to the class specification but that did not work either. What I did is as follows:

In the GetValueOfAverage method, I inserted the following code

var methodInfo = System.Reflection.MethodBase.GetCurrentMethod();
var fullName = methodInfo.DeclaringType.FullName + "." + methodInfo.Name;
string str = methodInfo.DeclaringType.FullName + ".";

before the line

Type calledType = Type.GetType(typeName, true);

and replaced the line

Type calledType = Type.GetType(typeName, true);

with

Type calledType = Type.GetType(str+typeName, true);

Can someone please help me understand what I am doing wrong? Any suggestions and/or advice would be greatly appreciated. Thank you.

Upvotes: 2

Views: 211

Answers (2)

Wojciech Kotlarski
Wojciech Kotlarski

Reputation: 256

For code loading class, please try to specify namespace like in example below:

movingAvg = GetValueOfAverage("ConsoleApplication1.MovingAverage", slowAvgMethod, slowArguments);

It may be still not enough if type is located in assembly that is currently not loaded yet. In this situation you may need to specify type with assembly qualified name like below:

movingAvg = GetValueOfAverage("ConsoleApplication1.MovingAverage, ConsoleApplication1", slowAvgMethod, slowArguments);

(where first ConsoleApplication1. is namespace, and second ConsoleApplication1 is assembly name)

Finally, you have a mistake in code calling method via reflection, where arguments are passed as nested object[] array. Arguments parameter is already passed as object[] so it should be passed directly to InvokeMember() method:

double avg  = (double)calledType.InvokeMember(
               methodName,
               BindingFlags.InvokeMethod | BindingFlags.Public | 
               BindingFlags.Static | BindingFlags.NonPublic,
               null,
               null,
               arguments);

I have updated this post with full example to visualize how to pass type. The code below is compiled to ConsoleApplication1.exe

using System;
using System.Reflection;

namespace MyNamespace
{
    public static class MovingAverage
    {
        public static double MyMethod(int Period, int CurrentBar, int[] Input, int[] average)
        {
            return 5;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var arguments = new object[] { 1, 2, new[] { 2 }, new[] { 4 } };

            Console.WriteLine(GetValueOfAverage("MyNamespace.MovingAverage", "MyMethod", arguments)); //if assembly is already loaded
            Console.WriteLine(GetValueOfAverage("MyNamespace.MovingAverage, ConsoleApplication1", "MyMethod", arguments)); //if assembly is not loaded yet
            Console.WriteLine(GetValueOfAverage(typeof(MovingAverage), "MyMethod", arguments)); //known type
            Console.ReadKey();
        }

        public static double GetValueOfAverage(string typeName, string methodName, object[] arguments)
        {
            // ... get the Type for the class
            Type calledType = Type.GetType(typeName, true);
            return GetValueOfAverage(calledType, methodName, arguments);
        }

        public static double GetValueOfAverage(Type calledType, string methodName, object[] arguments)
        {
            double avg = (double)calledType.InvokeMember(
                           methodName,
                           BindingFlags.InvokeMethod | BindingFlags.Public |
                           BindingFlags.Static | BindingFlags.NonPublic,
                           null,
                           null,
                           arguments);

            // Return the value returned by the called method.
            return avg;
        }
    }
}

Upvotes: 1

Michael
Michael

Reputation: 3150

You need to fully qualify the MovingAverage type

Type.GetType("MovingAverage");

won't work unless you have overridden the TypeResolve event.

You need to use

Type.GetType("MovingAverageNamespace.MovingAverage, MovingAverageDllWithoutExtension");

Upvotes: 0

Related Questions