Reputation: 1318
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
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
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
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