简体   繁体   中英

Using IL Emit to replace Activator.CreateInstance

I have a class implementing an interface, which has a multi-parameter constructor, and a static sorted collection. This class is a Base Class which has many inherited classes.

internal class SCO : IVotable
{
    public SCO(SPListItem item, List<Vote> votes)
    {
        //Initialize Object
    }

    public static List<T> SortedCollection<T>(SPListItemCollection items, ListSortType sortType, List<Vote> votes) where T : IVotable
    {
        var returnlist = new List<T>();
        Type genericType = typeof(T);
        for (int i = 0; i < items.Count; i++) { returnlist.Add((T)Activator.CreateInstance(genericType, new object[] { items[i], votes })); }
        switch (sortType)
        {
            case ListSortType.Hot:
                returnlist.Sort((p1, p2) => p2.HotScore.CompareTo(p1.HotScore));
                break;
            case ListSortType.Top:
                returnlist.Sort((p1, p2) => p2.VoteTotal.CompareTo(p1.VoteTotal));
                break;
            case ListSortType.Recent:
                returnlist.Sort((p1, p2) => p2.CreatedDate.CompareTo(p1.CreatedDate));
                break;
        }
        return returnlist;
    }
}

This allows me to do the following with any Child Class:

List<ChildClass> sortedClassList = ChildClass.SortedCollection<ChildClass>(listItems, sortType, votes);

My current reliance on Activator.CreateInstance worries me, as this is about 100 times slower than using Emit IL directly. I've been reading a few articles about Emit IL, and it seems fantastic for this solution.

I cannot seem to get it to work, however. When I try to instantiate ILGenerator gen = it tells me "Cannot access non-static field 'method' in static context"

My class isn't static, neither are my constructors, and the static list show below isn't interacting with Emit yet. How do I make this work?

Code thus far:

internal class SCO : IVotable
{
    //Properties emittied
    static ConstructorInfo ctor = typeof(SCO).GetConstructors()[1];
    delegate SCO SCOCtor(SPListItem item, List<Vote> votes);
    static SCOCtor SCOCtorDelegate;

    DynamicMethod method = new DynamicMethod("CreateInstance", typeof (SCO),
                             new Type[] {typeof (SPListItem), typeof (List<Vote>)});

    ILGenerator gen = method.GetILGenerator(); //Error here
    //"Cannot access non-static field 'method' in static context"

    private static SCO CreateInstance(SPListItem item, List<Vote> votes)
    {
        return SCOCtorDelegate(item, votes);
    }
}

Blog for reference: http://ayende.com/blog/3167/creating-objects-perf-implications

I have a drop-in replacement for Activator that uses IL generation hosted here on CodePlex. You can also get it via Nuget here (a single source file include, no assemblies).

The source code for FasterActivator is here .

Usage is something like what's outlined below.

private static readonly Dictionary<Type, DynamicCreationDelegate> _cachedCreationDelegates = new Dictionary<Type, DynamicCreationDelegate>();

private static DynamicCreationDelegate CreateOrGet(Type typeToCreate)
{
    DynamicCreationDelegate result = null;

    if (!_cachedCreationDelegates.TryGetValue(typeToCreate, out result))
    {
        result = FastActivator.GenerateDelegate(typeToCreate, 
        /* List of types that make up the constructor signature of the type being constructed */
        typeof(SPListItem), typeof(List<Vote>));
        _cachedCreationDelegates.Add(result);
    }

    return result;
}

// Usage 
for (int i = 0; i < items.Count; i++) 
{ 
    var creationDelegate = CreateOrGet(typeof(genericType));
    returnlist.Add((T)creationDelegate(new object[] { items[i], votes })); 
}

Oh, and here is a generic version that ought to be faster.

private static readonly Func<SPListItem, List<T>, T> _creationFunc;
private static Func<SPListItem, List<T>, T> CreateOrGetFunc()
{
    if (!_creationFunc == null)
        _creationFunc = FastActivator.GenerateFunc<Func<SPListItem, List<T>, T>>(/* IL generator knows all type info from generic params now */);

    return _creationFunc;
}

// Usage
for (int i = 0; i < items.Count; i++) 
{ 
    var creationFunc = CreateOrGetFunc();
    returnlist.Add(creationFunc(items[i], votes )); 
}

Hope this helps!

I see you've got a answer. I just uploaded to github the code of a helper class to IL constructor calls (I was supposed to have done this before, but were missing a good reason ):

The usage is something like this:

// suppose you want to call the constructor for this class  
// but generalizing the return to ISomeInterface
public class AClass : ISomeInterface
{
   public class(int intParam, String stringParam) { }
}

// construct the factory method Func<int, string, ISomeInterface>
var createAClassInstance = ReflectionHelper
   // return type + constructor params
   .CreateFactoryMethod<ISomeInterface, int, string>(typeof(AClass));

var instance = createAClassInstance(10, "hello constructor");

If you're willing to accept some repetition, then instead of dealing with IL (which can get complicated fast, as you discovered), you can use delegates. Something like:

internal class SCO : IVotable
{
    protected static List<T> SortedCollection<T>
        (SPListItemCollection items, ListSortType sortType, List<Vote> votes,
        Func<SPListItem, List<Vote>, T> factory) where T : IVotable
    {
        var returnlist = new List<T>();
        Type genericType = typeof(T);
        for (int i = 0; i < items.Count; i++)
            returnlist.Add(factory(items[i], votes));

        // etc.
    }
}

class ChildClass : SCO
{
    public static List<ChildClass> SortedCollection
        (SPListItemCollection items, ListSortType sortType, List<Vote> votes)
    {
        return SCO.SortedCollection<ChildClass>(
            items, sortType, votes, (i, vs) => new ChildClass(i, vs));
    }
}

Doing it this way should be very fast, probably even slightly faster than when using IL emit.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM