2012-01-11 1 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, 아니 Expando 사용. –

+0

그런 다음 사전을 사용하고 Reflection API로 메소드를 호출하면 어떨까요? 특정 CMS 클래스에 대해 임의의 웹 메서드 호출을 중앙 집중화하기 위해 CMS에서 동일한 것을 사용합니다. –

답변

1

여기에 다소 비슷한 문제와 해결책이 있습니다. .NET 3.5 용으로 작성되었으며 잘 작동합니다. 목표는 단일 웹 서비스 (.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