Use Compiled Lambda expression instead of Activator.CreateInstance to initialize SoapHttpClientProtocol object

activator c# expression-trees linq reflection

Question

I'm working with the code that dynamically instantiates SoapHttpClientProtocol object (proxy class) and uses this object to make a call to WS-Basic I Web Service. Here is the simplified version of my code:

public override object Call(Type callingObject,
string method, object[] methodParams, string URL)
{
    MethodInfo requestMethod = callingObject.GetMethod(method);

    //creates an instance of SoapHttpClientProtocol
    object instance = Activator.CreateInstance(callingObject);

    //sets the URL for the object that was just created 
    instance.GetType().InvokeMember("Url", 
    BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty, null,
    instance,
    new object[1] {URL});

    return requestMethod.Invoke(instance, methodParams); 
}

I've noticed that in some cases Activator.CreateInstance() call can take significant amount of time, so I'm trying to optimize the code by using a lambda expression:

public override object Call(Type callingObject,
string method, object[] methodParams, string URL)
{
    MethodInfo requestMethod = callingObject.GetMethod(method);

    //creates an instance of SoapHttpClientProtocol using compiled Lambda Expression
    ConstructorInfo constructorInfo = callingObject.GetConstructor(new Type[0]);
    object instance = Expression.Lambda(Expression.New(constructorInfo)).Compile();

    //sets the URL for the object that was just created 
    instance.GetType().InvokeMember("Url", 
    BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty, null,
    instance,
    new object[1] {URL});

    //calls the web service
    return requestMethod.Invoke(instance, methodParams); 
}

Unfortunately, this code does not create an object of the callingObject type (instead it returns a Func<T> delegate object) and therefore when it tries to set the Url in the next line it throws an exception:

System.MissingMethodException: Attempted to access a missing member.

Am I missing something in my code?

Thanks!

Accepted Answer

The Expression.Lambda(Expression.New(constructorInfo)).Compile() part returns a Func<T> delegate that wraps a constructor of Type stored in callingObject parameter. To actually call that constructor, you still need to invoke it:

Delegate delegateWithConstructor = Expression.Lambda(Expression.New(constructorInfo)).Compile();
object instance = delegateWithConstructor.DynamicInvoke();

However, what you are trying to do seems quite strange and fragile in the long run, since you are passing around method names as simple strings and parameters as objects, therefore losing all compile-time type checking. Why do you need to do it?


Popular Answer

Using expression trees will cause your program to run slower unless You cache compiled expression.



Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why