12

パフォーマンスが強化されたActivator.CreateInstance()が必要で、ファクトリを使用してILでインスタンスを作成し、それをキャッシュするMironAbramsonによるこの記事に出くわしました。(どういうわけか消えた場合に備えて、Miron Abramsonのサイトから以下のコードを含めました)。私はILEmitコードと、クラスをインスタンス化するためのActivator.CreateInstance()以外のものに不慣れであり、どんな助けでも大いに感謝するでしょう。

私の問題は、パラメーターを持つctorを受け取るオブジェクトのインスタンスを作成する必要があることです。パラメーターのタイプを渡す方法があるようですが、ctorパラメーターの値も渡す方法はありますか?

可能であれば、CreateObjectFactory<T>(params object[] constructorParams)インスタンス化する一部のオブジェクトに複数のctorパラメーターがある可能性があるのと同様のメソッドを使用したいと思います。


// Source: http://mironabramson.com/blog/post/2008/08/Fast-version-of-the-ActivatorCreateInstance-method-using-IL.aspx
public static class FastObjectFactory
{
    private static readonly Hashtable creatorCache = Hashtable.Synchronized(new Hashtable());
    private readonly static Type coType = typeof(CreateObject);
    public delegate object CreateObject();

    /// 
    /// Create an object that will used as a 'factory' to the specified type T 
   /// 
    public static CreateObject CreateObjectFactory() where T : class
    {
        Type t = typeof(T);
        FastObjectFactory.CreateObject c = creatorCache[t] as FastObjectFactory.CreateObject;
        if (c == null)
        {
            lock (creatorCache.SyncRoot)
            {
                c = creatorCache[t] as FastObjectFactory.CreateObject;
                if (c != null)
                {
                    return c;
                }
                DynamicMethod dynMethod = new DynamicMethod("DM$OBJ_FACTORY_" + t.Name, typeof(object), null, t);
                ILGenerator ilGen = dynMethod.GetILGenerator();

                ilGen.Emit(OpCodes.Newobj, t.GetConstructor(Type.EmptyTypes));
                ilGen.Emit(OpCodes.Ret);
                c = (CreateObject)dynMethod.CreateDelegate(coType);
                creatorCache.Add(t, c);
            }
        }
        return c;
    }
}

彼の投稿2010-01-11のコメント投稿者からのMironのコードへの更新

public static class FastObjectFactory2<T> where T : class, new()
{
    public static Func<T> CreateObject { get; private set; }

    static FastObjectFactory2()
    {
        Type objType = typeof(T);
        var dynMethod = new DynamicMethod("DM$OBJ_FACTORY_" + objType.Name, objType, null, objType);
        ILGenerator ilGen = dynMethod.GetILGenerator();
        ilGen.Emit(OpCodes.Newobj, objType.GetConstructor(Type.EmptyTypes));
        ilGen.Emit(OpCodes.Ret);
        CreateObject = (Func<T>)
        dynMethod.CreateDelegate(typeof(Func<T>));
    }
}
4

8 に答える 8

6

私はこれを使って少しテストを行ってきましたが、Mironの元の記事(ここ)のフォローアップとして、.NET4.0Activatorが以前よりもはるかに高速であることを発見しました。ミリ秒単位でタイミングを表示するように調整された彼のアプリのバージョンからのいくつかの結果:

.NET 3.5 build

Number of iterates: 1000000
Activator.CreateInstance(Type):                           4150
Activator.CreateInstance<T>():                            1288
FastObjectFactory.CreateObjec (empty cache):                33
FastObjectFactory.CreateObjec (cache full):                 28
ItemFactory.GetNewItem:                                   1283


.NET 4.0 build

Number of iterates: 1000000
Activator.CreateInstance(Type):                            138
Activator.CreateInstance<T>():                             151
FastObjectFactory.CreateObjec (empty cache):                28
FastObjectFactory.CreateObjec (cache full):                 22
ItemFactory.GetNewItem:                                    156

ただし、これはパラメーターなしのコンストラクターの場合であり、以下に示すように、パラメーター付きのコンストラクターを使用すると、アクティベーターがまだ少し遅いことに気付きました。

ここに投稿された元のソリューションで私が抱えていた問題の1つは、コンパイル時に必要なオブジェクトのタイプが必ずしもわからないことです。タイプ参照しかありません。今(私がダッファーでない限り)、これは、ここで一般的なソリューションまたはその単純なバリエーションを使用できないことを意味します。

だから、これは私が一緒にノックしたバージョンで、問題に対処しています。また、コンストラクターパラメーターを使用すると、.NET4.0Activatorの速度がわずかに遅くなります。

// For use with no-parameter constructors. Also contains constants and utility methods
public static class FastActivator
{
    // THIS VERSION NOT THREAD SAFE YET
    static Dictionary<Type, Func<object>> constructorCache = new Dictionary<Type, Func<object>>();

    private const string DynamicMethodPrefix = "DM$_FastActivator_";

    public static object CreateInstance(Type objType)
    {
        return GetConstructor(objType)();
    }

    public static Func<object> GetConstructor(Type objType)
    {
        Func<object> constructor;
        if (!constructorCache.TryGetValue(objType, out constructor))
        {
            constructor = (Func<object>)FastActivator.BuildConstructorDelegate(objType, typeof(Func<object>), new Type[] { });
            constructorCache.Add(objType, constructor);
        }
        return constructor;
    }

    public static object BuildConstructorDelegate(Type objType, Type delegateType, Type[] argTypes)
    {
        var dynMethod = new DynamicMethod(DynamicMethodPrefix + objType.Name + "$" + argTypes.Length.ToString(), objType, argTypes, objType);
        ILGenerator ilGen = dynMethod.GetILGenerator();
        for (int argIdx = 0; argIdx < argTypes.Length; argIdx++)
        {
            ilGen.Emit(OpCodes.Ldarg, argIdx);
        }
        ilGen.Emit(OpCodes.Newobj, objType.GetConstructor(argTypes));
        ilGen.Emit(OpCodes.Ret);
        return dynMethod.CreateDelegate(delegateType);
    }
}

// For use with one-parameter constructors, argument type = T1
public static class FastActivator<T1>
{
    // THIS VERSION NOT THREAD SAFE YET
    static Dictionary<Type, Func<T1, object>> constructorCache = new Dictionary<Type, Func<T1, object>>();
    public static object CreateInstance(Type objType, T1 arg1)
    {
        return GetConstructor(objType, new Type[] { typeof(T1) })(arg1);
    }
    public static Func<T1, object> GetConstructor(Type objType, Type[] argTypes)
    {
        Func<T1, object> constructor;
        if (!constructorCache.TryGetValue(objType, out constructor))
        {
            constructor = (Func<T1, object>)FastActivator.BuildConstructorDelegate(objType, typeof(Func<T1, object>), argTypes);
            constructorCache.Add(objType, constructor);
        }
        return constructor;
    }
}

// For use with two-parameter constructors, argument types = T1, T2
public static class FastActivator<T1, T2>
{
    // THIS VERSION NOT THREAD SAFE YET
    static Dictionary<Type, Func<T1, T2, object>> constructorCache = new Dictionary<Type, Func<T1, T2, object>>();
    public static object CreateInstance(Type objType, T1 arg1, T2 arg2)
    {
        return GetConstructor(objType, new Type[] { typeof(T1), typeof(T2) })(arg1, arg2);
    }

    public static Func<T1, T2, object> GetConstructor(Type objType, Type[] argTypes)
    {
        Func<T1, T2, object> constructor;
        if (!constructorCache.TryGetValue(objType, out constructor))
        {
            constructor = (Func<T1, T2, object>)FastActivator.BuildConstructorDelegate(objType, typeof(Func<T1, T2, object>), argTypes);
            constructorCache.Add(objType, constructor);
        }
        return constructor;
    }
}

// For use with three-parameter constructors, argument types = T1, T2, T3
// NB: could possibly merge these FastActivator<T1,...> classes and avoid generic type parameters
// but would need to take care that cache entries were keyed to distinguish constructors having 
// the same number of parameters but of different types. Keep separate for now.
public static class FastActivator<T1, T2, T3>
{
    // THIS VERSION NOT THREAD SAFE YET
    static Dictionary<Type, Func<T1, T2, T3, object>> constructorCache = new Dictionary<Type, Func<T1, T2, T3, object>>();
    public static object CreateInstance(Type objType, T1 arg1, T2 arg2, T3 arg3)
    {
        return GetConstructor(objType, new Type[] { typeof(T1), typeof(T2), typeof(T3) })(arg1, arg2, arg3);
    }

    public static Func<T1, T2, T3, object> GetConstructor(Type objType, Type[] argTypes)
    {
        Func<T1, T2, T3, object> constructor;
        if (!constructorCache.TryGetValue(objType, out constructor))
        {
            constructor = (Func<T1, T2, T3, object>)FastActivator.BuildConstructorDelegate(objType, typeof(Func<T1, T2, T3, object>), argTypes);
            constructorCache.Add(objType, constructor);
        }
        return constructor;
    }
}

以下のいくつかのパフォーマンス結果。これは、100万個のオブジェクトとタイミングをミリ秒単位で作成するためのものであることに注意してください。

Activator.CreateInstance(objType) - parameterless constructor: 153
FastActivator.CreateInstance(objType) - parameterless constructor: 86
Using FastActivator.GetConstructor and calling it repeatedly - parameterless constructor: 34
Activator.CreateInstance(objType) with 1 constructor arg: 3183
FastActivator.CreateInstance(objType) with 1 constructor arg: 257
FastActivator.GetConstructor and calling it repeatedly with 1 constructor arg: 126
Activator.CreateInstance(objType) with 3 constructor args: 4403
FastActivator.CreateInstance(objType) with 3 constructor args: 640
FastActivator.GetConstructor and calling it repeatedly with 3 constructor args : 405
FastActivator.GetConstructor and calling it repeatedly with 3 constructor args; args created only once : 19
于 2011-07-01T14:06:49.313 に答える
4

私のテストによると、現在の(2010-01-11)の回答を使用して、これまでのところ最高のパフォーマンスを発揮するオブジェクト作成ファクトリーとしてこれを掲げています。キャッシュの使用は、反復が99,999未満の場合に最適であることに気付きました。99,999を超える負荷をかける場合は、キャッシュを使用しないことをお勧めします。これは、キャッシュを使用できるかどうかを許可するものを作成した場合である可能性があるためです。私の現在のプロジェクトでは、数百万のレコードが読み込まれることもあれば、1つだけが読み込まれることもあります。とにかく、私はあなたの考えが何であるかを見るためにこれをそこに置いています。以下のコードは、1つの引数を持つctor用であり、複数のargctorに対して同様のファクトリを作成する必要があることに注意してください。


// code updated 2010-06-01
// class that creates comment objects
public class CreatesSomeObject
{
    // method that creates a comment object
    public void CreateComment()
    {

        // Method 1 (without cache)
        Comment comment1 = ObjectFactoryFactory<Comment, ObjectId>
            .CreateObject.Invoke(new ObjectId());

        // Method 2 (with cache)
        Comment comment2 = ObjectFactoryFactory<Comment, ObjectId>
            .CreateObjectWithCache.Invoke(new ObjectId());

        // Method 3 (without helper factory ObjectFactoryFactory)
        Comment comment3 = ObjectFactory<Comment, ObjectId>
            .CreateObject.Invoke(new ObjectId());
    }
}

// This is optional class. Just helps in creating objects when
// a cache is needed or not needed.
public static class ObjectFactoryFactory<T, P1> where T : class
{
    static Hashtable cache = Hashtable.Synchronized(new Hashtable());

    public static Func<P1, T> CreateObject
    {
        get { return ObjectFactory<T, P1>.CreateObject; }
    }

    public static Func<P1, T> CreateObjectWithCache
    {
        get
        {
            return ObjectFactory<T, P1>.UseCache(cache);
        }
    }
}

// Main object creation factory class.
public static class ObjectFactory<T, P1> where T : class
{

    static Func<P1, T> _createObject;

    public static Func<P1, T> CreateObject
    {
        get
        {
            if (_createObject != null) return _createObject;
            _createObject = CreateDelegate();
            return _createObject;
        }
    }

    static Func<P1, T> CreateDelegate()
    {
        Type objType = typeof(T);
        Type[] types = new[] { typeof(P1) };
        var dynMethod = new DynamicMethod("DM$OBJ_FACTORY_" + 
            objType.Name, objType, types, objType);
        ILGenerator ilGen = dynMethod.GetILGenerator();
        // if need more than 1 arg add another Ldarg_x
        // you'll also need to add proper generics and 
        // CreateDelegate signatures
        ilGen.Emit(OpCodes.Ldarg_0);
        ilGen.Emit(OpCodes.Newobj, objType.GetConstructor(types));
        ilGen.Emit(OpCodes.Ret);
        return (Func<P1, T>)dynMethod.CreateDelegate(typeof(Func<P1, T>));
    }

    public static Func<P1, T> UseCache(Hashtable cache) 
    { 
        Type t = typeof(T);
        Func<P1, T> c = cache[t] as Func<P1, T>;
        if (c == null) 
        { 
            lock (cache.SyncRoot) 
            {
                c = cache[t] as Func<P1, T>;
                if (c != null) 
                { 
                    return c; 
                } 
                c = CreateDelegate(); 
                cache.Add(t, c); 
            } 

        } 
        return c; 
    }
}
于 2010-01-11T22:07:32.803 に答える
1

少し前に書いた。ILを発行するのではなく、.NET 3.5 Linq式ツリーを使用しています。ILはほぼ同じくらい高速で、保守が容易です。最大4つのコンストラクター引数を取ることができます。

必要なコンストラクター引数を使用すると、引数の型に基づいてコンストラクターを検索するため、少し遅くなる可能性がありますが、それでもリフレクションを使用するよりもはるかに高速です。また、IL放出では、いくつかのルックアップが必要になると思います。

IOC / DIコンテナではないため、構築する正確なタイプを指定する必要があります。多分あなたはそれをあなたのニーズに拡張して適応させることができます。

// usage:
Cat myCat = Instantiator<Cat>.New("furry", /* isCute*/ true);

using System;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

static public class Instantiator<TInstance>
{
    static Instantiator()
    {
        Debug.Assert(typeof(TInstance).IsValueType || (typeof(TInstance).IsClass && !typeof(TInstance).IsAbstract),
                String.Concat("The type ", typeof(TInstance).Name, " is not constructable."));
    }

    static public TInstance New()
    {
        return InstantiatorImpl.CtorFunc();
    }

    static public TInstance New<TA>(TA valueA)
    {
        return InstantiatorImpl<TA>.CtorFunc(valueA);
    }

    static public TInstance New<TA, TB>(TA valueA, TB valueB)
    {
        return InstantiatorImpl<TA, TB>.CtorFunc(valueA, valueB);
    }

    static public TInstance New<TA, TB, TC>(TA valueA, TB valueB, TC valueC)
    {
        return InstantiatorImpl<TA, TB, TC>.CtorFunc(valueA, valueB, valueC);
    }

    static public TInstance New<TA, TB, TC, TD>(TA valueA, TB valueB, TC valueC, TD valueD)
    {
        return InstantiatorImpl<TA, TB, TC, TD>.CtorFunc(valueA, valueB, valueC, valueD);
    }

    static private Expression<TDelegate> CreateLambdaExpression<TDelegate>(params Type[] argTypes)
    {
        Debug.Assert(argTypes != null);

        ParameterExpression[] paramExpressions = new ParameterExpression[argTypes.Length];

        for (int i = 0; i < paramExpressions.Length; i++)
        {
            paramExpressions[i] = Expression.Parameter(argTypes[i], String.Concat("arg", i));
        }

        ConstructorInfo ctorInfo = typeof(TInstance).GetConstructor(argTypes);
        if (ctorInfo == null)
        {
            throw new ArgumentException(String.Concat("The type ", typeof(TInstance).Name, " has no constructor with the argument type(s) ", String.Join(", ", argTypes.Select(t => t.Name).ToArray()), "."),
                    "argTypes");
        }

        return Expression.Lambda<TDelegate>(Expression.New(ctorInfo, paramExpressions), paramExpressions);
    }

    static private class InstantiatorImpl
    {
        static public readonly Func<TInstance> CtorFunc = Expression.Lambda<Func<TInstance>>(Expression.New(typeof(TInstance))).Compile();
    }

    static private class InstantiatorImpl<TA>
    {
        static public readonly Func<TA, TInstance> CtorFunc = Instantiator<TInstance>.CreateLambdaExpression<Func<TA, TInstance>>(typeof(TA)).Compile();
    }

    static private class InstantiatorImpl<TA, TB>
    {
        static public readonly Func<TA, TB, TInstance> CtorFunc = Instantiator<TInstance>.CreateLambdaExpression<Func<TA, TB, TInstance>>(typeof(TA), typeof(TB)).Compile();
    }

    static private class InstantiatorImpl<TA, TB, TC>
    {
        static public readonly Func<TA, TB, TC, TInstance> CtorFunc = Instantiator<TInstance>.CreateLambdaExpression<Func<TA, TB, TC, TInstance>>(typeof(TA), typeof(TB), typeof(TC)).Compile();
    }

    static private class InstantiatorImpl<TA, TB, TC, TD>
    {
        static public readonly Func<TA, TB, TC, TD, TInstance> CtorFunc = Instantiator<TInstance>.CreateLambdaExpression<Func<TA, TB, TC, TD, TInstance>>(typeof(TA), typeof(TB), typeof(TC), typeof(TD)).Compile();
    }
}

それでfunzを持ってください!:->

于 2010-01-08T16:56:49.343 に答える
1

私は通常Activator.CreateInstance()、ジェネリッククラスで完全に回避するために次のメソッドを使用します。

Func<T>デリゲートをコンストラクターに渡す必要があります。Tの新しいインスタンスをインスタンス化する必要がある場合、クラスはこれを呼び出します。これにより、非ジェネリッククラスと同じくらい迅速にインスタンス化が行われ、ILを発行する複雑なクラスよりもシンプルでエレガントなIMOになります。もう1つの利点(New T())このメソッドを使用する場合のVSは、パラメーター化されたコンストラクターを使用してクラスをインスタンス化できることです。

編集:BillWの要求に従って、パラメーター化されたコンストラクターの例でコードを更新しました。

class GenericClass<T>
{
    public GenericClass(Func<T> classFactory)
    {
        this.ClassFactory = classFactory;
    }
    Func<T> ClassFactory;

    public void CreateNewInstDemo()
    {
        //T NewObject = New T(); // <- calls the slow Activator.CreateInstance() in IL
        T NewObject = ClassFactory(); // Calls the quick IL mnemonic 'newobj'
    }
}
class GenericClassParamsDemo<T>
{
    public GenericClassParamsDemo(Func<int, T> classFactory)
    {
        this.ClassFactory = classFactory;
    }
    Func<int, T> ClassFactory;

    public void CreateNewInstDemo()
    {
        T NewObject = ClassFactory(5); // Calls the quick IL mnemonic 'newobj'
    }
}
class ClassToCreate
{
    public int Number { get; set; }
    public ClassToCreate()
    {
        // Default constructor
    }
    public ClassToCreate(int number)
    {
        // Constructor With Parameter
        this.Number = number;
    }
}
class Program
{
    static void Main(string[] args)
    {
        GenericClass<ClassToCreate> gc =
        new GenericClass<ClassToCreate>(() => { return new ClassToCreate(); });
        // Call method which uses delegate to create a new instance
        gc.CreateNewInstDemo();

        GenericClassParamsDemo<ClassToCreate> gcParams =
        new GenericClassParamsDemo<ClassToCreate>((number) => { return new ClassToCreate(number); });
         // Call method which uses delegate to create a new instance with params
        gcParams.CreateNewInstDemo();
    }
}
于 2011-07-01T18:00:55.923 に答える
1

ジェネリッククラスで新しいT()が遅いとは思いもしませんでした。私の問題は本当に別のものです-コンパイル時にTが何であるかを知っていれば問題ありませんが、実行時に構成情報で指定されたクラス(つまり、アセンブリ/クラス名を保持する文字列)をインスタンス化する簡単な方法が必要です。Typeオブジェクトのキャッシュを使用して、アセンブリをロードし、その中の型を1回だけ見つけます。そのため、最後のハードルは、以前の投稿の主題であったインスタンスをすばやく作成することです。

とにかく、この種のことで.NET 4.0の方が速いという私の発見に続いて、私はあなたの例のバージョンでテストし、各CreateNewInstxxxメソッドを1,000,000回呼び出しました。ミリ秒単位のタイミング:

class GenericClass<T> where T : new()
{
    Func<T> ClassFactory;    
    public GenericClass(Func<T> classFactory)    
    {        
        this.ClassFactory = classFactory;    
    }    
    public T CreateNewInstQuick()    
    {        
        return ClassFactory(); // Calls the quick IL mnemonic 'newobj'   
    }
    public T CreateNewInstStd()
    {
        return new T(); // <- calls the slow Activator.CreateInstance() in IL
    }
}

.NET 3.5
CreateNewInstQuick: 35
CreateNewInstStd: 1298

.NET 4.0
CreateNewInstQuick: 29
CreateNewInstStd: 165

そうです、.NET4.0は以前よりもはるかに高速です。CreateNewInstStd()メソッド用にコンパイラーによって生成されたコードは、どちらの場合も次のようになります。したがって、スピードアップは改善されたActivator.CreateInstance<T>()メソッドによるものと思われます。

public T CreateNewInstStd()
{
    T t1 = default(T);
    if (t1 != null)
    {
        T t2 = default(T);
        return t2;
    }
    return Activator.CreateInstance<T>();
}

編集:GenericClassがこれを介して参照型に制約されている場合、そのパフォーマンスを追加するには、ほぼ同じです。

class GenericClass<T> where T :  class, new()

その場合、コンパイラによって生成されたコードは次のようになります。

public T CreateNewInstStd()
{
    return Activator.CreateInstance<T>();
}
于 2011-07-04T12:24:07.070 に答える
0

CreateObject最初に変更する必要があるのは代理人だと思います。私はこのようにします:

public delegate object CreateObject(params object[] args);

次に、 (式ツリーを使用して)CreateObjectからデリゲートを生成する良い方法は次のとおりです。ConstructorInfo

/// <summary>
///    Creates and compiles an Expression like this:
/// (object[] args) =>
/// (object)(
///     new {ConstructedType}(args[0], args[1], ...)
/// )
/// </summary>
public static DynamicStaticMethod CreateConstructor(ConstructorInfo constructor)
{
    if (constructor == null) throw new ArgumentNullException("constructor");

    //Create 'args' parameter expression
    ParameterExpression argsParameter = Expression.Parameter(typeof(object[]), "args");

    //Create body expression
    ParameterInfo[] constructorParams = constructor.GetParameters();
    Expression body = Expression.New(
        constructor,
        CreateParameterExpressions(constructorParams, argsParameter)
    );

    //Create and compile lambda
    var lambda = Expression.Lambda<CreateObject>(
        Expression.Convert(body, typeof(object)),
        argsParameter
    );
    return lambda.Compile();
}

このコンパイラの完全な拡張バージョンは、私の個人リポジトリにあります。そこにあるものは何でも自由に閲覧してください。

更新:リポジトリリンクが変更されました。

于 2010-01-08T20:39:07.257 に答える
0

単純なアプローチを使用しない場合、プロファイラーはActivator.CreateInstance()(およびそれによって呼び出されるコンストラクターではない)に多くの時間が費やされていることを示していますか?そうでない場合は、Activator.CreateInstanceを使用してください。(ctor引数を持つジェネリックCreateInstance()メソッドはないようですが、非ジェネリックメソッドがあります)。

于 2010-01-10T02:03:43.540 に答える
0

フォローするのは、@thamesの回答をVB.NETに変換したものです。

//' Original source: http://stackoverflow.com/questions/2024435/how-to-pass-ctor-args-in-activator-createinstance-or-use-il/2045313#2045313
//' Converted to VB with: http://www.developerfusion.com/tools/convert/csharp-to-vb/
//' code updated 2010-06-01
//' class that creates comment objects
Public Class CreatesSomeObject

    //' method that creates a comment object
    Public Sub CreateComment()

        //' Method 1 (without cache)
        Dim comment1 As Comment = ObjectFactoryFactory(Of Comment, ObjectId).CreateObject.Invoke(New ObjectId())

        //' Method 2 (with cache)
        Dim comment2 As Comment = ObjectFactoryFactory(Of Comment, ObjectId).CreateObjectWithCache.Invoke(New ObjectId())

        //' Method 3 (without helper factory ObjectFactoryFactory)
        Dim comment3 As Comment = ObjectFactory(Of Comment, ObjectId).CreateObject.Invoke(New ObjectId())

    End Sub

End Class

Public Class Comment

    Public Sub New(ByVal objectId As ObjectId)
    End Sub

End Class

Public Class ObjectId
End Class

//' This is optional class. Just helps in creating objects when
//' a cache is needed or not needed.
Public NotInheritable Class ObjectFactoryFactory(Of T As Class, P1)

    Private Sub New()
    End Sub

    Shared cache As Hashtable = Hashtable.Synchronized(New Hashtable())

    Public Shared ReadOnly Property CreateObject() As Func(Of P1, T)
        Get
            Return ObjectFactory(Of T, P1).CreateObject
        End Get
    End Property

    Public Shared ReadOnly Property CreateObjectWithCache() As Func(Of P1, T)
        Get
            Return ObjectFactory(Of T, P1).UseCache(cache)
        End Get
    End Property
End Class

//' Main object creation factory class.
Public NotInheritable Class ObjectFactory(Of T As Class, P1)

    Private Sub New()
    End Sub

    Shared _createObject As Func(Of P1, T)

    Public Shared ReadOnly Property CreateObject() As Func(Of P1, T)
        Get
            If _createObject IsNot Nothing Then
                Return _createObject
            End If
            _createObject = CreateDelegate()
            Return _createObject
        End Get
    End Property

    Private Shared Function CreateDelegate() As Func(Of P1, T)

        Dim objType As Type = GetType(T)
        Dim types As Type() = {GetType(P1)}
        Dim dynMethod = New DynamicMethod("DM$OBJ_FACTORY_" + objType.Name, objType, types, objType)
        Dim ilGen As ILGenerator = dynMethod.GetILGenerator()

        //' if need more than 1 arg add another Ldarg_x
        //' you'll also need to add proper generics and 
        //' CreateDelegate signatures
        ilGen.Emit(OpCodes.Ldarg_0)
        ilGen.Emit(OpCodes.Newobj, objType.GetConstructor(types))
        ilGen.Emit(OpCodes.Ret)

        Return DirectCast(dynMethod.CreateDelegate(GetType(Func(Of P1, T))), Func(Of P1, T))

    End Function

    Public Shared Function UseCache(ByVal cache As Hashtable) As Func(Of P1, T)

        Dim t As Type = GetType(T)
        Dim c As Func(Of P1, T) = TryCast(cache(t), Func(Of P1, T))

        If c Is Nothing Then

            SyncLock cache.SyncRoot

                c = TryCast(cache(t), Func(Of P1, T))
                If c IsNot Nothing Then
                    Return c
                End If
                c = CreateDelegate()
                cache.Add(t, c)

            End SyncLock

        End If

        Return c

    End Function

End Class

コメントに//'を使用すると、SOコードカラーライザーが正しく機能することに注意してください。このコードをプロジェクトに貼り付けたら、//'を'に置き換えます。

于 2010-11-03T20:22:58.730 に答える