2017-10-03 7 views
1

特定のインターフェイスを実装する型をインスタンス化するオブジェクトファクトリを作成しようとしていますが、インスタンス化する型が実行時に不明で、反射。私はいくつかの例を見つけましたが、これは表現を使用して可能かもしれませんが、私の場合に適した例を見つけることはできませんでした。単にそれが可能ではないかもしれないかもしれないかもしれないが、私は確かめるためにここに質問を投げたかった。C#式をリフレクションなしで動的にインスタンス化する式

は、だから私が持っているもの、これはここまでです:

public static Func<Type, object[], IMyInterface> FactoryExpression = 
    Expression.Lambda<Func<Type, object[], IMyInterface>>(
     /* Something that creates an instance of the type with given arguments */ 
    ).Compile() 

public static IMyInterface GetTypeOfMyInterface() 
{ 
    Type t = Type.GetType(GetTypeNameFromSomewhere()); 
    ConstructorInfo c = t.GetConstructors().First(); 
    object[] args = ResolveCostructorArguments(c.GetParameters()); 

    return FactoryExpression(t, args); 
} 

私は式のこれらのタイプの少しのexprienceを持っています。これをすべて動作させることは可能なのでしょうか、あるいは私は反射に落ちなければなりませんか?

EDIT:ジョン・ハンナの例を使用することにより

私は、次のを思い付いた:

public class TypeInitializer<TResult> 
{ 
    private static readonly ConcurrentDictionary<string, Func<object[], TResult>> InstanceCreationMethods = 
     new ConcurrentDictionary<string, Func<object[], TResult>>(); 

    public static TResult CreateInstance(ConstructorInfo constructorInfo, params object[] arguments) 
    { 
     ParameterInfo[] parameterInfo = constructorInfo.GetParameters(); 
     IEnumerable<Type> parameterTypes = parameterInfo.Select(p => p.ParameterType); 
     string constructorSignatureKey = GetConstructorSignatureKey(constructorInfo.DeclaringType, parameterTypes); 

     Func<object[], TResult> factoryMethod = InstanceCreationMethods.GetOrAdd(constructorSignatureKey, key => 
                             { 
                              Expression[] args = new Expression[parameterInfo.Length]; 
                              ParameterExpression param = Expression.Parameter(typeof(object[])); 
                              for (int i = 0; i < parameterInfo.Length; i++) 
                               args[i] = Expression.Convert(Expression.ArrayIndex(param, Expression.Constant(i)), parameterInfo[i].ParameterType); 
                              return Expression 
                               .Lambda<Func<object[], TResult>>(Expression.Convert(Expression.New(constructorInfo, args), typeof(TResult)), param) 
                               .Compile(); 
                             }); 

     return factoryMethod(arguments); 
    } 

    private static string GetConstructorSignatureKey(Type type, IEnumerable<Type> argumentTypes) => string.Concat(type.FullName, " (", string.Join(", ", argumentTypes.Select(at => at.FullName)), ")"); 
} 

意図したとおりにexacly動作するように思われます!それに感謝します。私も違いを見るためにActivator.CreateInstanceなどconstructorInfo.Invokeと構築されたSOMのパフォーマンステストを使用して実装を行った実験の便宜上 ...

00:00:00.9246614, Actiator 
00:00:00.7524483, Constructor Invoke 
00:00:00.8235814, Compiled Expression 

このテストでは、100個の000インスタンスの作成を時限それぞれのメソッドを使用して同じ型を作成し、結果を出力します。私はコンストラクタがメソッドの継ぎ目をよりよく実行するのを見るのが少し奨励されました!

+2

[リフレクションを使用して型を動的にインスタンス化する方法](https://stackoverflow.com/questions/5493881/how-to-instantiate-a-type-dynamically-using-reflection) – rmjoia

+0

リフレクションはありません私は何をしています。 –

+0

ああ、私の悪い、私はGetTypeNameFromSomwhere()を見た...まあ、文字列から型をインスタンス化すると、データベースのプロセスのIDを取得していました。そのIDによって、新しい型をインスタンス化していました。 文字列からインスタンス化する方法があります。 :p – rmjoia

答えて

2

Expression.Newを使用してインスタンスを作成できます。

また、ConstructorInfoを工場に渡し、Expression.Convertを使用してオブジェクトをインターフェイスにキャストする必要があります。

public static Func<object[], IMyInterface> BuildFactoryExpression(ConstructorInfo ctor) 
{ 
    ParameterInfo[] par = ctor.GetParameters(); // Get the parameters of the constructor 
    Expression[] args = new Expression[par.Length]; 
    ParameterExpression param = Expression.Parameter(typeof(object[])); // The object[] paramter to the Func 
    for (int i = 0; i != par.Length; ++i) 
    { 
     // get the item from the array in the parameter and cast it to the correct type for the constructor 
     args[i] = Expression.Convert(Expression.ArrayIndex(param, Expression.Constant(i)), par[i].ParameterType); 
    } 
    return Expression.Lambda<Func<object[], IMyInterface>>(
     // call the constructor and cast to IMyInterface. 
     Expression.Convert(
      Expression.New(ctor, args) 
     , typeof(IMyInterface) 
     ), param 
    ).Compile(); 
} 

はい、私はかなり簡単であるだけでなく反射でこの種の問題を解決してきました。しかし、この場合、可能な限りパフォーマンスの低下を避けるために探しています。

これはまだリフレクションを使用しています。同じFuncを繰り返し使用する場合は、Compile()がILにコンパイルするコンテキストで実行しています(例:UWPではなく)。リフレクションが再利用可能なデリゲートを作成するために一度使用されるため、ここから取得できますデリゲートをC#のメソッドとして記述したのと同じです。 object[]を渡すのではなく、あなたの議論を打ち込むことができれば、さらにそれを得ることができます。

これらのデリゲートを一度に1つずつ使用している場合は、リフレクションを使用するだけでもよいでしょう。式の代理人が解釈されるコンテキストにのみ参加する場合は、とにかく内部的にリフレクションを使用する必要があります。

関連する問題