2012-01-11 9 views
1

私が取り組んでいる問題(.NET 3.5 WinFormsアプリケーション)の解決策を考えています。Reflection.Emitのパフォーマンスペナルティ

私たちのアプリでは、アプリケーションのユーザが引数を入力する多くのメソッド(C#)があります。

名、日付が現在の単純なテキストボックスを使用して入力され
public void DoSomething(string name, DateTime date) 
{ 
    // ... 
} 

例は次のようなものになるだろう。 リッチエディタ、パスワードで保護された入力ボックス、オートコンプリートなどのメリットがあります。

PropertyGridを使用してユーザーの入力を希望しますが、このコントロールはオブジェクトにのみバインドでき、引数にはバインドできません。

私はProperyGridに関するMSDNの雑誌からの両方の優れた記事を読んだ:

ICustomTypeDescriptor, Part 1

ICustomTypeDescriptor, Part 2

は、しかし、これはオブジェクトPropertyGridのにバインドされるシナリオで有用であるように思わ私の場合ではない、事前に知られています。

このシナリオはサポートできますか?シンプルで簡単に実装できるソリューションはありますか?

Reflection.Emitを使用して実行時にプロパティのメソッドの引数となる "temp"オブジェクトを作成すると考えました。 私はReflection.Emit名前空間を使用する前にこれをしたことがありませんでした。これを使用した場合のパフォーマンス上のペナルティを知りたいですか? (実際に実行時にコードをコンパイルするか、それはどのように動作するのですか?)

+0

あなたは、このような用途にはExpandoオブジェクトを使用するために停止しませんか? –

+0

私の質問を編集しました - 3.5、no Expandoを使用してください。 –

+0

次に、辞書を使ってReflection APIでメソッドを呼び出すのはどうですか?私は、特定のクラスメソッドに任意のWebメソッド呼び出しを集中するために、CMSで同じことを使用します。 –

答えて

1

ここで多かれ少なかれ同じ問題とその解決策があります。これは.NET 3.5用に書かれており、うまく機能します。目標は、すべてのWebメソッドを単一のWebサービス(.asmx)に集約し、登録されたメソッドを単一の場所から呼び出すことでした。コードははるかに小さくすることができます。しかし、いくつかの強制的な変換のために、それは少し長いです。

public object ExecuteMethod(string moduleName, string methodName, object[] arguments) 
{ 
    CmsModuleMethodInfo methodInfo = CmsModuleManager.GetModuleMethod(moduleName, methodName); 
    ... 

    ParameterInfo[] paramInfo = methodInfo.Method.GetParameters(); 
    Object[] parameters = new Object[paramInfo.Length]; 
    Type[] paramTypes = paramInfo.Select(x => x.ParameterType).ToArray(); 
    for (int i = 0; i < parameters.Length; ++i) 
    { 
    Type paramType = paramTypes[i]; 
    Type passedType = (arguments[i] != null) ? arguments[i].GetType() : null; 

    if (paramType.IsArray) 
    { 
     // Workaround for InvokeMethod which is very strict about arguments. 
     // For example, "int[]" is casted as System.Object[] and 
     // InvokeMethod raises an exception in this case. 
     // So, convert any object which is an Array actually to a real Array. 
     int n = ((Array)arguments[i]).Length; 
     parameters[i] = Array.CreateInstance(paramType.GetElementType(), n); 
     Array.Copy((Array)arguments[i], (Array)parameters[i], n); 
    } 
    else if ((passedType == typeof(System.Int32)) && (paramType.IsEnum)) 
    { 
     parameters[i] = Enum.ToObject(paramType, (System.Int32)arguments[i]); 
    } 
    else 
    { 
     // just pass it as it's 
     parameters[i] = Convert.ChangeType(arguments[i], paramType); 
    } 
    } 

    object result = null; 
    try 
    { 
    result = methodInfo.Method.Invoke(null, parameters); 
    } 
    catch (TargetInvocationException e) 
    { 
    if (e.InnerException != null) 
    { 
     throw e.InnerException; 
    } 
    } 

    return result; 
} 
+1

Osman、リフレクションを使用してメソッドを呼び出す場合、パフォーマンスが大幅に低下します。 FastMethodInvoker(www.codeproject.com/KB/cs/FastMethodInvoker.aspx)のようなものをまだ検討していない場合は、考慮する必要があります –

+0

@Joe:ありがとうございました!実際、私たちはどんな遅さも感じません。そういうものを知っていることは明らかに良いことです。もう一度ありがとうございます。 –

3

はい、Reflection.Emitを使用して、これを行うことができます(メソッドパラメータに対応するプロパティを持つプロキシタイプを作成できます)。これを取得したら、プロキシオブジェクトのインスタンスをPropertyGridに割り当ててから、入力した値を使用してメソッドを呼び出すことができます。しかし、あなたがしたいことは、自明ではありません。

Reflection.Emitを使用して型を作成する例については、TypeBuilderのMSDNドキュメントを参照してください。

パフォーマンスに関する質問に答えるために、コードは「メモリ内に」コンパイルされています。通常は、生成された型を辞書にキャッシュして再利用できるようにします。パフォーマンスの最大のヒットはタイプ生成です。タイプのインスタンスを作成することは、あなたがどのようにそれを行うかに応じて - Activatorを非常に安くすることができます。CreateInstance()、このような何か最も遅いである:

private Func<T> GetCreator() 
    { 
     if (_Creator == null) 
     { 
      Expression body; 
      var bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; 
      var defaultConstructor = typeof(T).GetConstructor(bindingFlags, null, new Type[0], null); 
      if (defaultConstructor != null) 
      { 
       // lambdaExpression =() => (object) new TClass() 
       body = Expression.New(defaultConstructor); 
      } 
      else 
      { 
       // lambdaExpression =() => FormatterServices.GetUninitializedObject(classType) 
       var getUnitializedObjectMethodInfo = typeof(FormatterServices).GetMethod("GetUninitializedObject", BindingFlags.Public | BindingFlags.Static); 
       body = Expression.Call(getUnitializedObjectMethodInfo, Expression.Constant(typeof(T))); 
      } 
      var lambdaExpression = Expression.Lambda<Func<T>>(body); 
      _Creator = lambdaExpression.Compile(); 
     } 
     return _Creator; 
    } 

あなたは、単にアプリケーションがあるとき、あなたはパフォーマンス低下を参照してくださいよ、このパターンを使用して

object obj = GetCreator()(); 

を呼び出して新しいインスタンスを作成することができますちょうど始めますが、キャッシュミスが減るにつれてインラインコードとほとんど同じ性能を発揮します。

あなたは、呼び出し元を生成するための同様の方法を使用することができます - かなり良い例がここにあります:

http://www.codeproject.com/KB/cs/FastMethodInvoker.aspx

関連する問題