Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Instantiating a class using Reflection

Suppose I have three projects in my sln.

(1) xyz.a{Class Lib}{no reference added}
(2) yzx.b{Class Lib}{added the reference of xyz.a}
(3) zxy.c{Console App}{added the reference of xyz.a}

Now, I need to create the instance of a class residing in yzx.b from within xyz.a using reflection.

And also this should be independent of the folder/directory-names.

I.e. even If I change the name of the directory of yzx.b, it should work.

Does anyone have any idea?

like image 523
user366312 Avatar asked Aug 28 '26 01:08

user366312


2 Answers

First of all, Activator.CreateInstance() is a right way.

But, there is a more interesting way that is:

  • 10 times faster
  • Don't wrap exceptions in TargetInvocationException

Just create expression that calls constructor:

public static Func<object[], object> CreateConstructorDelegate(ConstructorInfo method)
{
        var args = Expression.Parameter(typeof(object[]), "args");

        var parameters = new List<Expression>();

        var methodParameters = method.GetParameters().ToList();
        for (var i = 0; i < methodParameters.Count; i++)
        {
            parameters.Add(Expression.Convert(
                               Expression.ArrayIndex(args, Expression.Constant(i)),
                               methodParameters[i].ParameterType));
        }

        var call = Expression.Convert(Expression.New(method, parameters), typeof(object));

        Expression body = call;

        var callExpression = Expression.Lambda<Func<object[], object>>(body, args);
        var result = callExpression.Compile();

        return result;
}

Performance test:

    public void activator()
    {
        var stopwatch = new Stopwatch();
        const int times = 10000000;

        stopwatch.Start();
        for (int i = 0; i < times; i++)
        {
            var v = Activator.CreateInstance(typeof (C));
        }
        stopwatch.Stop();

        Console.WriteLine(stopwatch.ElapsedMilliseconds + "ms with activator");

        var del = CreateConstructorDelegate(typeof(C).GetConstructor(new Type[0]));

        stopwatch = new Stopwatch();
        stopwatch.Start();

        var args = new object[0];

        for (int i = 0; i < times; i++)
        {
            var v = del(args);
        }

        stopwatch.Stop();

        Console.WriteLine(stopwatch.ElapsedMilliseconds + "ms with expression");
    }

Output:

1569ms with activator
134ms with expression

But:

  • C# 3.0 only
  • Complile() is long running operation

Just for curious.

like image 91
gimalay Avatar answered Aug 30 '26 15:08

gimalay


You might want to check out the Activator.CreateInstance() methods. Just pass it the name of the assembly and type.

If you don't have a compile-time reference to the assembly, you can still reference it at runtime with Assembly.Load().

like image 22
Spodi Avatar answered Aug 30 '26 14:08

Spodi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!