私たちのアプリケーションのパフォーマンスを向上させようとしています。悲しみを引き起こしている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"));
上記を支援するオブジェクトを作成するより良い方法はありますか?具体的な種類がわからないときは、少し難しいです。
これらの間のベンチマークをいくつか実行しました(最低限の詳細を書き留めます)。
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
。したがって、空の文字列を返すために、stringに対する特別な処理が行われます。
typeof(T)
) - nawfal
これは役に立つかもしれません: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();
問題は、結果をどこかに保存してその結果を何度も使用するのではなく、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();
}
DelegateStore<T>
そのジェネリッククラスは自動的にユニークなパーT
すでにインスタンス化されています。それがそれであってもこれは本当ですstatic
クラス。だからあなたが必要とするすべてDelegateStore<T>
単純ですinternal static Func<T> _cached_func;
あなたがそれを持っているので、あなたはたくさんの辞書を作成しています。T
- それぞれにキャッシュされたデリゲートを1つだけ含みます。 - Glenn Slayden
あなたはおそらく同じコードの生成からいくらかのオーバーヘッドを得ています。
のILGenerator
ファクトリ用のコードを動的に作成します。
地図を作成するDictionary
すでに使用した型のうち、ファクトリメソッドをその型用に作成したままにします。
コンストラクタを直接呼び出す、デリゲートを構築するための汎用メソッド。 指定されたデリゲート型のシグネチャを使用して、指定された型のコンストラクタを自動的に検索し、その型のデリゲートを構築します。ここにコード:
/// <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つのデリゲートを使います。
CreateInstance
- その重要な点は、あなたが電話をかけることです。CreateInstance
一度だけ覚えている工場の代理人。電話している場合CreateInstance
各操作で、はい、遅くなります... - Jon Skeet