2

In the past, I have written an application that dynamically loads a DLL and all of it's dependencies based upon a value in a database, as below (sanitised some parameters)

VB

Dim oType As System.Type
Dim oAssembly As System.Reflection.Assembly
Dim oObject As System.Object
oAssembly = Assembly.LoadFrom(path)
oType = oAssembly.GetType("LogicValidator")
oObject = Activator.CreateInstance(oType)

oObject.Initialise("param1", "param2", "param3", AuditTrail, UserInfo, workingDir)
Console.WriteLine("Begin processing...")
oObject.ProcessBatch(cm_uid)

Now, when I write something similar in C#, an error is present before building the application, stating the following:

C#

System.Type oType = default(System.Type);
System.Reflection.Assembly oAssembly = default(System.Reflection.Assembly);
System.Object oObject = null;
oAssembly = Assembly.LoadFrom(path);
oType = oAssembly.GetType("LogicValidator");
oObject = Activator.CreateInstance(oType);
oObject.Initialise("param1", "param2", "param3", AuditTrail, UserInfo, workingDir);
Console.WriteLine("Begin processing...");
oObject.ProcessBatch(cm_uid);

Error

'object' does not contain a definition for 'Initialise' and no extension method 'Initialise' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?)

How can my C# application identify that a method does not exist before compiling, if it is loaded at runtime?

Edit

I have struck out the below for the purposes of helping others who stumble across this question. Whilst it does point to the answer, others have answered it more eloquently. :-)

I would like to add, that the below works - but only if the loaded assemblies dependencies are already present.

var DLL = Assembly.LoadFile(path);
Type type = DLL.GetType("LogicValidator");
dynamic c = Activator.CreateInstance(type);
c.Initialise("param1", "param2", "param3", audit, UserInfo, workingDir);
Console.WriteLine("Begin processing...");
c.ProcessBatch(cm_uid);

IronAces
  • 1,857
  • 1
  • 27
  • 36
  • 1
    Don't use `System.Object` for the type of `oObject`, use the actual type you need (i.e. `LogicValidator`). Otherwise `dynamic` may be your best bet. – DavidG Nov 22 '16 at 13:51

3 Answers3

3

If you don't know the type at compile time you can make use of the dynamic runtime by declaring the type to be dynamic:

var oAssembly = Assembly.LoadFrom(path);
var oType = oAssembly.GetType("LogicValidator");
dynamic oObject = Activator.CreateInstance(oType);
oObject.Initialise("param1", "param2", "param3", AuditTrail, UserInfo, workingDir);
Console.WriteLine("Begin processing...");
oObject.ProcessBatch(cm_uid);

However, you might consider an approach where the loaded type implements a known interface (which would be declared in an assembly that is referenced both by the add-in and your code):

public interface IProcessor
{
    void Initialise(
        string param1, 
        string param2, 
        string param2, 
        AuditTail auditTrail, 
        UserInfo userInfo, 
        DirectoryInfo workingDir);

    void ProcessBatch(int uid);
}

You can then create an instance as follows and get full IntelliSense support and type checking:

var oAssembly = Assembly.LoadFrom(path);
var oType = oAssembly.GetType("LogicValidator");
IProcessor oObject = Activator.CreateInstance(oType);
oObject.Initialise("param1", "param2", "param3", AuditTrail, UserInfo, workingDir);
Console.WriteLine("Begin processing...");
oObject.ProcessBatch(cm_uid);  
Dirk Vollmar
  • 172,527
  • 53
  • 255
  • 316
0

You need to change oObject data type to dynamic as follows:

System.Type oType = default(System.Type);
System.Reflection.Assembly oAssembly = default(System.Reflection.Assembly);
dynamic oObject = null;
oAssembly = Assembly.LoadFrom(path);
oType = oAssembly.GetType("LogicValidator");
oObject = Activator.CreateInstance(oType);
oObject.Initialise("param1", "param2", "param3", AuditTrail, UserInfo, workingDir);
Console.WriteLine("Begin processing...");
oObject.ProcessBatch(cm_uid);

More about dynamic: dynamic (C# Reference)

Wagner DosAnjos
  • 6,304
  • 1
  • 15
  • 29
0

You don't need to cast it to dynamic as the other answers say, you can also continue to use reflection which won't invoke the DLR and should be quicker in this instance, especially since you're already loading the necessary reflection objects:

var oAssembly = Assembly.LoadFrom(path);
var oType = oAssembly.GetType("LogicValidator");
var oObject = Activator.CreateInstance(oType);
var oInitialiseMethod = oType.GetMethod("Initialise"); //See note below the code.
var oProcessBatchMethod = oType.GetMethod("ProcessBatch"); //See note below the code.
oInitialiseMethod.Invoke(oObject, "param1", "param2", "param3", AuditTrail, UserInfo, workingDir);
Console.WriteLine("Begin processing...");
oObject.ProcessBatch(cm_uid);

You'll want to use the overload for Type.GetMethod that accepts a Type[] for the function parameters so that the signatures match. Id've done it for you but I don't know what types some of those are.

See https://msdn.microsoft.com/en-us/library/system.type.getmethod(v=vs.110).aspx

As another answer said, though, an interface is a good solution here too - depending on the overall structure of the application and if it's feasible to do for your scenario.

Yushatak
  • 741
  • 1
  • 5
  • 15