37

アプリケーションのパフォーマンスを改善しようとしています。多くの Activator.CreateInstance 呼び出しがあり、問題を引き起こしています。

インターフェイス (ITabDocument) に基づいて多くのクラスをインスタンス化し、いろいろ調べた後、次のコードを使用することを考えました。

このコードは、私たちが持っていた Activator.CreateInstance コードを使用するよりも優れているわけではありません (実際、わずかに遅くなります)。

    public static Func<T> CreateInstance<T>(Type objType) where T : class, new()
    {
        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);
        return (Func<T>)dynMethod.CreateDelegate(typeof(Func<T>));
    }

これがなぜなのか疑問に思っています。私がやっていることは次のとおりです。

ITabDocument document = CreateInstance<ITabDocument>(Type.GetType("[Company].Something"));

上記を支援するオブジェクトを作成するより良い方法はありますか? 具体的なタイプがわからない場合は少し難しいです。

4

6 に答える 6

54

これらの間でいくつかのベンチマークを行いました(最低限の詳細を書き留めます):

public static T Instance() //~1800 ms
{
    return new T();
}

public static T Instance() //~1800 ms
{
    return new Activator.CreateInstance<T>();
}

public static readonly Func<T> Instance = () => new T(); //~1800 ms

public static readonly Func<T> Instance = () => 
                                 Activator.CreateInstance<T>(); //~1800 ms

//works for types with no default constructor as well
public static readonly Func<T> Instance = () => 
               (T)FormatterServices.GetUninitializedObject(typeof(T)); //~2000 ms


public static readonly Func<T> Instance = 
     Expression.Lambda<Func<T>>(Expression.New(typeof(T))).Compile();  
     //~50 ms for classes and ~100 ms for structs

CD が言うように、コンパイルされた式は最速であり、大きな差があります。を除くすべてのメソッドは(T)FormatterServices.GetUninitializedObject(typeof(T)) 、既定のコンストラクターを持つ型に対してのみ機能します。

また、ジェネリック型ごとに静的クラスがある場合、コンパイルされた結果のデリゲートをキャッシュするのは簡単です。お気に入り:

public static class New<T> where T : new()
{
    public static readonly Func<T> Instance = Expression.Lambda<Func<T>>
                                              (
                                               Expression.New(typeof(T))
                                              ).Compile();
}

new制約に注意してください。なんでも電話

MyType me = New<MyType>.Instance();

クラスが初めてメモリにロードされるときを除いて、実行は最速になります。

デフォルトのコンストラクターを使用する場合と使用しない場合の両方の型を処理するクラスを作成するために、ここからハイブリッド アプローチを採用しました。

public static class New<T>
{
    public static readonly Func<T> Instance = Creator();

    static Func<T> Creator()
    {
        Type t = typeof(T);
        if (t == typeof(string))
            return Expression.Lambda<Func<T>>(Expression.Constant(string.Empty)).Compile();

        if (t.HasDefaultConstructor())
            return Expression.Lambda<Func<T>>(Expression.New(t)).Compile();

        return () => (T)FormatterServices.GetUninitializedObject(t);
    }
}

public static bool HasDefaultConstructor(this Type t)
{
    return t.IsValueType || t.GetConstructor(Type.EmptyTypes) != null;
}

値型も効率的に処理します。

(T)FormatterServices.GetUninitializedObject(t)は失敗することに注意してくださいstring。したがって、空の文字列を返すために、文字列の特別な処理が用意されています。

于 2013-04-23T06:52:50.760 に答える
22

これは役立つかもしれません:Activator.CreateInstanceまたはConstructorInfo.Invokeを使用せず、コンパイルされたラムダ式を使用してください

// Make a NewExpression that calls the ctor with the args we just created
NewExpression newExp = Expression.New(ctor, argsExp);                  

// Create a lambda with the New expression as body and our param object[] as arg
LambdaExpression lambda = Expression.Lambda(typeof(ObjectActivator), newExp, param);            


// Compile it
ObjectActivator compiled = (ObjectActivator)lambda.Compile();
于 2011-07-05T12:00:14.250 に答える
9

問題は、結果をどこかに保存してその結果を何度も何度も使用するのではなく、CreateInstance を直接何度も呼び出す場合は、先に進んで内部にキャッシュする必要があることです。

internal static class DelegateStore<T> {
     internal static IDictionary<string, Func<T>> Store = new ConcurrentDictionary<string,Func<T>>();
}

public static T CreateInstance<T>(Type objType) where T : class
{
    Func<T> returnFunc;
    if(!DelegateStore<T>.Store.TryGetValue(objType.FullName, out returnFunc)) {
        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);
        returnFunc = (Func<T>)dynMethod.CreateDelegate(typeof(Func<T>));
        DelegateStore<T>.Store[objType.FullName] = returnFunc;
    }
    return returnFunc();
}
于 2011-07-11T13:28:08.857 に答える
3

同じコードを生成することで、おそらくオーバーヘッドが発生します。

ファクトリのILGeneratorコードを動的に作成します。

ある種のマップまたはDictionaryすでに使用したタイプを作成し、そのタイプ用に作成されたファクトリメソッドを保持します。

于 2011-07-05T12:05:25.790 に答える
0

コンストラクターを直接呼び出して、デリゲートを構築するためのジェネリック メソッド。指定されたデリゲート タイプのシグネチャを持つ指定されたタイプのコンストラクターを自動的に検索し、そのタイプのデリゲートを構築します。ここにコード:

/// <summary>
/// Reflective object construction helper.
/// All methods are thread safe.
/// </summary>
public static class Constructor
{
    /// <summary>
    /// Searches an instanceType constructor with delegateType-matching signature and constructs delegate of delegateType creating new instance of instanceType.
    /// Instance is casted to delegateTypes's return type. 
    /// Delegate's return type must be assignable from instanceType.
    /// </summary>
    /// <param name="delegateType">Type of delegate, with constructor-corresponding signature to be constructed.</param>
    /// <param name="instanceType">Type of instance to be constructed.</param>
    /// <returns>Delegate of delegateType wich constructs instance of instanceType by calling corresponding instanceType constructor.</returns>
    public static Delegate Compile(Type delegateType,Type instanceType)
    {
        if (!typeof(Delegate).IsAssignableFrom(delegateType))
        {
            throw new ArgumentException(String.Format("{0} is not a Delegate type.",delegateType.FullName),"delegateType");
        }
        var invoke = delegateType.GetMethod("Invoke");
        var parameterTypes = invoke.GetParameters().Select(pi => pi.ParameterType).ToArray();
        var resultType = invoke.ReturnType;
        if(!resultType.IsAssignableFrom(instanceType))
        {
            throw new ArgumentException(String.Format("Delegate's return type ({0}) is not assignable from {1}.",resultType.FullName,instanceType.FullName));
        }
        var ctor = instanceType.GetConstructor(
            BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, parameterTypes, null);
        if(ctor == null)
        {
            throw new ArgumentException("Can't find constructor with delegate's signature","instanceType");
        }
        var parapeters = parameterTypes.Select(Expression.Parameter).ToArray();

        var newExpression = Expression.Lambda(delegateType,
            Expression.Convert(Expression.New(ctor, parapeters), resultType),
            parapeters);
        var @delegate = newExpression.Compile();
        return @delegate;
    }
    public static TDelegate Compile<TDelegate>(Type instanceType)
    {
        return (TDelegate) (object) Compile(typeof (TDelegate), instanceType);
    }
}

Yappiプロジェクトのソースの一部です。これを使用すると、パラメーターを持つコンストラクターを含む、指定された型の任意のコンストラクターを呼び出すデリゲートを構築できます (ref および out パラメーターを除く)。

使用例:

var newList = Constructor.Compile<Func<int, IList<String>>>(typeof (List<String>));
var list = newList(100);

デリゲートの構築後、静的辞書のどこかに、またはジェネリックパラメーターを持つクラスの静的フィールドに保存します。毎回新しいデリゲートを構築しないでください。特定の型の複数のインスタンスを構築するために 1 つのデリゲートを使用します。

于 2011-07-30T11:41:59.960 に答える