2011-02-28 20 views
11

Estoy tratando de encontrar una solución para 'romper en métodos no públicos'.Llamada al método privado que retiene la pila de llamadas

Solo quiero llamar al RuntimeMethodInfo.InternalGetCurrentMethod(...), pasando mi propio parámetro (para que pueda implementar GetCallingMethod()), o directamente use RuntimeMethodInfo.InternatGetCurrentMethod(ref StackCrawlMark.LookForMyCaller) en mis rutinas de registro. GetCurrentMethod se implementa como:

[MethodImpl(MethodImplOptions.NoInlining)] 
public static MethodBase GetCurrentMethod() 
{  
    StackCrawlMark lookForMyCaller = StackCrawlMark.LookForMyCaller;  
    return RuntimeMethodInfo.InternalGetCurrentMethod(ref lookForMyCaller); 
} 

donde se declara InternalGetCurrentMethod: :-) interna.

No tengo ningún problema para llamar al método usando la reflexión, pero esto arruina la pila de llamadas y eso es lo único que se debe preservar, de lo contrario, infringe su propósito.

¿Cuáles son mis probabilidades de mantener el StackTrace cerca de la original (al menos dentro de la distancia de los permitidos StackCrawlMark s, que son LookForMe, LookForMyCaller y LookForMyCallersCaller. ¿Hay alguna manera compleja para lograr lo que quiero?

Respuesta

17

Si hay una cosa que me gusta de C#, es métodos dinámicos

Ellos permiten omitir todos los objetivos y la intención de los creadores de .NET:.. D

Aquí hay una solución (thread-safe):

(Eric Lippert, por favor, no leen esto ...)

enum MyStackCrawlMark { LookForMe, LookForMyCaller, LookForMyCallersCaller, LookForThread } 
delegate MethodBase MyGetCurrentMethodDelegate(ref MyStackCrawlMark mark); 
static MyGetCurrentMethodDelegate dynamicMethod = null; 

static MethodBase MyGetCurrentMethod(ref MyStackCrawlMark mark) 
{ 
    if (dynamicMethod == null) 
    { 
     var m = new DynamicMethod("GetCurrentMethod", 
      typeof(MethodBase), 
      new Type[] { typeof(MyStackCrawlMark).MakeByRefType() }, 
      true //Ignore all privilege checks :D 
     ); 
     var gen = m.GetILGenerator(); 
     gen.Emit(OpCodes.Ldarg_0); //NO type checking here! 
     gen.Emit(OpCodes.Call, 
      Type.GetType("System.Reflection.RuntimeMethodInfo", true) 
       .GetMethod("InternalGetCurrentMethod", 
        BindingFlags.Static | BindingFlags.NonPublic)); 
     gen.Emit(OpCodes.Ret); 
     Interlocked.CompareExchange(ref dynamicMethod, 
      (MyGetCurrentMethodDelegate)m.CreateDelegate(
       typeof(MyGetCurrentMethodDelegate)), null); 
    } 
    return dynamicMethod(ref mark); 
} 
[MethodImpl(MethodImplOptions.NoInlining)] 
static void Test() 
{ 
    var mark = MyStackCrawlMark.LookForMe; //"Me" is Test's _caller_, NOT Test 
    var method = MyGetCurrentMethod(ref mark); 
    Console.WriteLine(method.Name); 
} 
+0

Esto es super cool. Tengo que leer más sobre métodos dinámicos. Eric Lippert puede estar enviando androides asesinos a tu casa, así que no voy a abrir la puerta por un tiempo. –

+1

@Justin: Mientras estén programados con C#, sabré cómo manejarlos. >:] – Mehrdad

+0

Impresionante ... ¡Funciona como un encanto! Y tan fácil ... ¡Así que finalmente puedo registrar el método sin pasar System.Reflection.MethodBase.GetCurrentMethod() al método de registro! Definitivamente intentaré esto como un reemplazo de reflexión en otros lugares también. Por cierto: para aquellos que leen la solución: el nombre del método dado al nuevo constructor DynamicMethod puede ser lo que quieras. – Edwin

Cuestiones relacionadas