Riddick
Riddick

Reputation: 1318

Get Constructor Matching Parameters

I have a method which takes in a constructor arguments as

Expression<func<T>>

thus declared as

object[] _constructorArgs = () => new MyClass("StringParam", 56, "ThirdParam");

I would instantiate this object as;

Activator.CreateInstance(typeof(MyClass), _constructorArgs);

Which works fine, but it's a bit slow.

Is there a way I can gather the types Constructor based on the contents of _constructorArgs so I would be able to call

ConstructorInfo.Invoke(_constructorArgs);

?

I know the Invoke is possible this way, it's just about finding the correct constructor based on the parameters in _constructorArgs.

Edit - For clarity

Apologies, I was very tired when I first asked this and should have given it more thought.

What I am doing is the following;

    public object Create<T>(Expression<Func<T>> constructor)
    {
        //I didn't include this step in the original code
        var constructorArguments =
            (((NewExpression)constructor.Body).Arguments.Select(
                argument => Expression.Lambda(argument).Compile().DynamicInvoke())).ToArray();

        object[] _args = constructorArguments;

        return Activator.CreateInstance(typeof(T), _args);
    }

However, if I do the following instead;

         ConstructorInfo c = type.GetConstructors().FirstOrDefault();

        //Get the types constructor
        return c.Invoke(_args);

I get better performance, I'm talking the first taking about 2800 milliseconds over a million iterations, using Invoke bringing that down to about 1000 milliseconds, thus 2.8 times faster.

This will work great if the first constructor always matches the arguments given, but this won't always be the case.

I want to know how I can get the correct ConstructorInfo based on arguments given.

Upvotes: 3

Views: 3379

Answers (3)

Nico
Nico

Reputation: 160

Might be a bit late, but I found a nice implementation with caching, which claims to be 70 times faster thant Activator.CreateInstance;

this.Item = (T)Activator.CreateInstance(typeof(T), new Object[] { x, y }, null); // Classical approach
this.Item = Constructor<Func<int,int,T>>.Ctor(x,y); // Dynamic constructor approach

The complete implementation can be found here: http://www.cyberforum.ru/blogs/32756/blog2078.html

Upvotes: 0

Luaan
Luaan

Reputation: 63732

If a million new objects per second isn't fast enough for you, you're going to have to go deeper. You need to start caching things. The simplest thing to cache is the constructor itself, so that you don't have to search for the correct constructor all the time. However...

Why are you doing this? Why don't you simply call the lambda outright? You've got all the code to instantiate the class, and then you throw it away and use Activator.CreateInstance? Why? Even if you do that, you don't need to search for the constructor - NewExpression.Constructor has the ConstructorInfo you need. Just do

((NewExpression)constructor.Body).Constructor.Invoke(_args) 

and you're done, no searching needed. All the metadata is already there in the expression tree.

Please explain why you can't simply do return constructor(); (with caching if possible / needed - it's handy to pass things as lambda parameters, since you can then easily cache the method itself).

Upvotes: 1

C.Evenhuis
C.Evenhuis

Reputation: 26446

Activator.CreateInstance() under the hood calls GetConstructors() and iterates over them to find the matching ones. This would explain the difference in performance - and if you roll your own implementation chances are you will end up with the same or worse performance.

You could simplify the process by comparing types using parameterType.IsAssignableFrom(argType), and return the first match - you may end up using a different constructor than Activator.CreateInstance() though because it uses the best match, not the first match:

class DerivedClass : BaseClass { }

class Test
{
    public Test(BaseClass c)
    {
    }

    public Test(DerivedClass c)
    {
    }
}

// Uses the most specific constructor, Test(DerivedClass):
Activator.CreateInstance(typeof(Test), new DerivedClass());

Upvotes: 1

Related Questions