How create High Performance .NET classes using ref

2019-03-26 06:43发布

问题:

Ok, so we all know Reflecttion is many time less performant than "newing" a class instance, and in many cases this is just fine depending on the application requirements.

QUESTION: How can we create high performance .NET classes using a late binding (Reflection) strategy.

I have an existing requirement that demands class instances be created using reflection (CreateInstance), but performance is critical. In my situation I am creating instances for every incoming SMS Message in our application. During production this could easily be over a million per day.

I would like to hear and share some ideas on how to create .NET classes without directly referencing the classes in code, for example using Reflection. I was also thinking if there is a way to somehow cache a class Factory that can improve the "Creation" time

回答1:

1 million a day is not a lot; I'd just use Activator.CreateInstance (a quick test using Activator.CreatInstance(Type) shows that on my lowly laptop it can create 1M objects from aType in ~2s).

Thoughts on creating objects quickly:

  • use generics and the : new() constraint (zero effort)
  • use DynamicMethod and write the IL (not hard)

An implementation of the new approach (without needing the : new() constraint externally) is shown here: ObjectFactory.cs.

For an IL example, see dapper-dot-net and il.Emit(OpCodes.Newobj, ...)



回答2:

I don't think a million per day is too much for a simple reflection call. I believe you are over-optimizing but anyway, as you said, just create a factory class using a single Activator.CreateInstance call and cache that one. Actual instances will be created using the CreateInstance() method call on the returned object.

public interface IClassFactory {
    IClass CreateInstance();
}

public interface IClass {
   // your actual class interface.
}

public class DefaultClassFactory : IClassFactory {
    public IClass CreateInstance() {
        return new DefaultClass(); // the implementation class
    }
}

Somewhere you'll have a static field of type IClassFactory which you'll set once with an instance of the DefaultClassFactory or any other classes specified in config file or whatever.



回答3:

Some thoughts:

  • Keep one instance of each class around, once you find you need it. Then, instead of CreateInstance, Clone it.
  • Once you've created the first instance, keep the Type of the instance around. then use Activator.CreateInstance(Type)

Cache the instance to clone or the Type in a Dictionary<string,Type> or Dictionary<string,object>.



回答4:

As is usually the case, Jon Skeet is your friend here. See his blog post Making reflection fly and exploring delegates



回答5:

GREAT! The Class Factory approach seems to be the way to go here.

Using a combination of Assembly.CreateInstance(typeNameString) on the first request, then cache Type in the factory.

On subsequent calls use Activator.CreateInstance(type).

Using this approach it is 20% slower than using a native New operator. No big deal there!

Stats for creating of 10 million Employee objects as follows:

  • 8 seconds using the new operator

  • 10 seconds using the Factory / Type / Cache approach.

Here is the sample code if anyone is interested:

private IEmployee CachedClassFactory()
{
    if(_typeCache == null)
    {
        // This is a one time hit to load the type into the cache
        string typeName = "ClassFactoryTest.Employee";
        string assemblyName = "ClassFactoryTest";
        Assembly assembly = Assembly.Load(assemblyName);
        IEmployee employee = assembly.CreateInstance(typeName) as IEmployee;                        
        _typeCache = employee.GetType();
    }

    IEmployee instance = Activator.CreateInstance(_typeCache) as IEmployee;

    instance.FirstName = "Raiford";
    instance.LastName = "Brookshire";
    instance.Birthdate = DateTime.Now.AddYears(-35);
    instance.Age = 35;

    return instance;    
}


回答6:

Define and implement an interface instead of using reflection.