2010-01-14 20 views
11

Uno puede enumerar el llamado método de los tipos de parámetros/información como esta:método de cómo enumerar pasan los parámetros

private void SomeMethod(int thisValue, string thatValue) 
{ 
    StackTrace stackTrace = new StackTrace(); 
    foreach (ParameterInfo pInfo in stackTrace.GetFrame(0).GetMethod().GetParameters()) 
    { 
    string name = pInfo.Name; 
    string type = pInfo.GetType().ToString(); 
    } 
} 

Pero, ¿hay alguna manera de conseguir el objeto real de cada parámetro?

EDIT: Mi objetivo es enumerar todos los parámetros y obtener sus valores. mediante LINQ expresiones, se puede obtener el valor del parámetro de este modo:

private void SomeMethod(int thisValue, string thatValue) 
{ 
    object valueOfThis = GetParameterValue(() => thisValue); 
    object valueOfThat = GetParameterValue(() => thatValue); 
} 
private object GetParameterValue<T>(Expression<Func<T>> expr) 
{ 
    var body = ((MemberExpression)expr.Body); 
    return ((FieldInfo)body.Member).GetValue(((ConstantExpression)body.Expression).Value); 
} 

Pero lo que me gustaría hacer es algo como:

foreach (fooObject o in thisMethod.GetParameterObjects()) 
{ 
    object someValue = GetParameterValue(() => fooObject); 
} 

y por lo tanto tener un método genérico para la recolección de todos los parámetros y sus valores

+0

¿Está intentando crear un seguimiento de pila que contenga los valores de parámetro reales? –

+1

Uhm, las instancias de parámetros reales están ahí en el método ... –

+1

@Mark: mi pensamiento también, pero viendo que es obvio que está buscando algo más general, algo que le permita obtener los valores de cualquier marco de pila (Supongo ...) –

Respuesta

9

ACTUALIZACIÓN:

Parece que la respuesta inicial "demasiado complicado" al tratar de explicar todo. Aquí está la versión corta de la respuesta.

private static void SomeMethod(int thisValue, string thatValue) 
{ 
    IEnumerable<object> parameters = GetParameters(() => SomeMethod(thisValue, thatValue)); 
    foreach (var p in parameters) 
     Console.WriteLine(p); 
} 
private static IEnumerable<object> GetParameters(Expression<Action> expr) 
{ 
    var body = (MethodCallExpression)expr.Body; 
    foreach (MemberExpression a in body.Arguments) 
    { 
     var test = ((FieldInfo)a.Member).GetValue(((ConstantExpression)a.Expression).Value); 
     yield return test; 
    } 
} 

Y aquí es la versión larga con algunas explicaciones.

De hecho, si usa árboles de expresiones, no necesita estar dentro de un método para enumerar sus parámetros.

static void Main(string[] args) 
    { 

     // First approach. 
     IEnumerable<object> parameters = GetParametersFromConstants(() => SomeMethod(0, "zero")); 
     foreach (var p in parameters) 
      Console.WriteLine(p); 

     // Second approach. 
     int thisValue = 0; 
     string thatValue = "zero"; 
     IEnumerable<object> parameters2 = GetParametersFromVariables(() => SomeMethod(thisValue, thatValue)); 
     foreach (var p in parameters2) 
      Console.WriteLine(p); 

     Console.ReadLine(); 
    } 

    private static void SomeMethod(int thisValue, string thatValue) 
    { 
     Console.WriteLine(thisValue + " " + thatValue); 
    }  

    private static IEnumerable<object> GetParametersFromVariables(Expression<Action> expr) 
    { 
     var body = (MethodCallExpression)expr.Body; 
     foreach (MemberExpression a in body.Arguments) 
     {    
      var test = ((FieldInfo)a.Member).GetValue(((ConstantExpression)a.Expression).Value); 
      yield return test; 
     } 
    } 

    private static IEnumerable<object> GetParametersFromConstants(Expression<Action> expr) 
    { 
     var body = (MethodCallExpression)expr.Body; 
     foreach (ConstantExpression a in body.Arguments) 
     { 
      var test = a.Value; 
      yield return test; 
     } 
    } 

} 

Tenga en cuenta que si usa árboles de expresiones, su código depende mucho de una expresión que se haya pasado al método. He mostrado uno usando constantes y uno usando variables. Pero, por supuesto, puede haber más escenarios. Puede refactorizar este código para usar un método único para ambos casos, pero decidí que esto ilustra mejor el problema de esta manera.

+0

Gracias por la respuesta. Sin embargo, estaba buscando algo que podría ponerse dentro de SomeMethod() para hacerlo genérico. El ejemplo que proporcionó obliga a uno a llamar al código personalizado antes de SomeMethod(). –

+0

No, no necesita llamar nada antes de SomeMethod. Tome el código marcado como "segundo enfoque" (simplemente omita la declaración de variables y la inicialización) y péguelo al método. Obtendrá la enumeración de parámetros dentro del método. Solo quería señalar que puedes hacerlo desde cualquier lugar, no necesariamente desde el cuerpo del método. –

+0

Si tiene que explicitar de alguna manera una lista de los parámetros en el código fuente, ¿por qué molestarse en tener 'GetParameters' en absoluto? Esto hará: 'object [] params = new object [] {thisValue, thatValue}; ' –

1

Bien, este es el trato.

Usted no puede hacer eso, no desde un lenguaje administrado. No veo cómo alguien te permitiría tomar el control del marco de pila. Y de una manera que es lo que quieres. Porque necesita la información para obtener los valores.

Ahora el tiempo de ejecución lo sabe, tiene toda la información, pero no puede hacer suposiciones sobre cómo se creará un marco de pila, porque no está destinado a hacer esto.

Ergo, sólo hay una manera de ir sobre esto. La API de creación de perfiles.

termino here. Dentro de las funciones de la API de creación de perfiles. Apuesto a que hay una forma de hacerlo que permite profundizar en los valores de los parámetros invocando una clase no administrada desde el código administrado.

Ahora, yo no haría esto porque ya hay grandes herramientas de perfilado, JetBrains dotTrace para nombrar una y con IntelliTrace en VS2010 todos estos dolores de cabeza simplemente desaparecerán ... IntelliTrace le permitirá realizar la depuración de viaje en el tiempo.

La otra y obvia forma de hacerlo es totalmente tonta, pero podría ser divertido experimentar con ella, siempre se puede hacer de esta manera, pero nunca en mi vida pondría este código en un entorno de producción.

// compile with unsafe 
unsafe 
{ 
    var p = stackalloc int[1]; 
    var baseAddr = p - sizeof(int); 
} 

Ahora, no se puede escribir en baseAddr pero se debe permitir que leerlo. La parte difícil es dar sentido a los marcos de la pila y que tiene que ver con la convención de llamadas y que debe saber con certeza una cabeza de tiempo. Here's un agotamiento de esas cosas y es de llamada rápida.

Con esta información y los objetos ParameterInfo usted debe ser capaz de caminar su camino a través de los argumentos.

Dado que trabajará con punteros crudos, tendrá que convertirlos en objetos administrados, y hay un class para eso.

¡Ahí va, se vuelven locos!

Una gran advertencia, aunque, lo que encontrarás a medida que subas la pila, no será lo que esperas. Debido a que los argumentos se pueden colocar en los registros y los registros no se puede acceder desde el código administrado.

1

Puede usar MethodInfo.GetCurrentMethod().GetParameters() para obtener una lista de los parámetros del método. Pero es imposible obtener sus valores por reflexión.

+1

@Mehdi. Sí, el método GetParameters() ya estaba cubierto en mi pregunta, gracias. –

Cuestiones relacionadas