2010-01-14 19 views
11

Si può enumerare la chiamata tipi di parametri metodo/informazioni come questo:metodo come enumerare passati parametri

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(); 
    } 
} 

Ma esiste un modo per ottenere l'oggetto effettivo di ciascun parametro?

EDIT: Il mio obiettivo è quello di elencare tutti i parametri e ottenere i loro valori. utilizzando LINQ espressioni, si può ottenere il valore del parametro in questo 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); 
} 

Ma quello che mi piacerebbe fare è qualcosa di simile:

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

E in tal modo avere un metodo generico per la raccolta di tutti i parametri e i loro valori.

+0

Si sta tentando di creare una traccia stack contenente i valori dei parametri effettivi? –

+1

Uhm, le istanze dei parametri effettivi sono proprio lì nel metodo ... –

+1

@Mark: anche il mio pensiero, ma visto che è ovvio che sta cercando qualcosa di più generale, qualcosa che ti permetterebbe di ottenere i valori di qualsiasi frame dello stack (Sto indovinando ...) –

risposta

9

UPDATE:

Sembra che la prima risposta "troppo complicata", cercando di spiegare tutto. Ecco la versione breve della risposta.

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; 
    } 
} 

E qui è la versione lunga con alcune spiegazioni.

Infatti, se si utilizzano gli alberi di espressioni, non è necessario essere all'interno di un metodo per enumerarne i parametri.

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; 
     } 
    } 

} 

Nota: se si utilizzano alberi di espressioni, il codice dipende molto da un'espressione passata al metodo. Ne ho mostrato uno usando le costanti e l'altro usando le variabili. Ma ovviamente ci possono essere più scenari. Puoi refactoring questo codice per utilizzare un singolo metodo per entrambi i casi, ma ho deciso che illustra il problema in questo modo.

+0

Grazie per la risposta. Tuttavia, stavo cercando qualcosa che potesse essere inserito in SomeMethod() per renderlo generico. L'esempio che hai fornito obbliga uno a chiamare il codice personalizzato prima di SomeMethod(). –

+0

No, non è necessario chiamare nulla prima di SomeMethod. Prendi il codice contrassegnato come "secondo approccio" (salta semplicemente la dichiarazione delle variabili e l'inizializzazione) e incollalo nel metodo. Otterrai l'enumerazione dei parametri all'interno del metodo. Volevo solo sottolineare che puoi farlo da qualsiasi luogo, non necessariamente dal corpo del metodo. –

+0

Se è necessario esplicitare in qualche modo i parametri nel codice sorgente, perché preoccuparsi di avere 'GetParameters' affatto? Questo farà: 'object [] params = new object [] {thisValue, thatValue}; ' –

1

Ok, quindi ecco l'accordo.

Non è possibile farlo, non da una lingua gestita. Non vedo come qualcuno ti permetterebbe di prendere il controllo del frame dello stack. E in un modo questo è quello che vuoi. Perché hai bisogno delle informazioni per ottenere i valori.

Ora il runtime lo sa, ma ha tutte le informazioni, ma non si può fare ipotesi su come andrà a creare uno stack frame, perché non sono destinati a fare questo.

Ergo, c'è solo un modo per farlo. L'API di profilazione.

Finisco here. All'interno delle funzioni dell'API di profilazione. Scommetto che c'è un modo per farlo che ti permette di scavare nei valori dei parametri invocando una classe non gestita dal codice gestito.

Ora, non vorrei fare questo perché non c'è grandi strumenti di profiling già là fuori, JetBrains dotTrace per citarne uno e con IntelliTrace in VS2010 tutti questi mal di testa sarà semplicemente andare via ... IntelliTrace ti consente di fare il tempo di viaggio di debug.

L'altro e ovvio modo di farlo è totalmente foobar, ma potrebbe essere divertente da sperimentare, può sempre essere fatto in questo modo, ma non metterei mai questo codice in un ambiente di produzione.

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

Ora, non è possibile scrivere a baseAddr ma si dovrebbe essere consentito di leggerlo. La parte più difficile è quella di dare un senso ai frame dello stack e questo deve essere fatto con la convenzione di chiamata e devi sapere per certo un certo anticipo. Here's a corto di quella roba ed è una chiamata rapida.

Con queste informazioni e gli oggetti ParameterInfo si dovrebbe essere in grado di farsi strada tra gli argomenti.

Dato che lavorerai con i puntatori grezzi dovrai renderli in oggetti gestiti, e c'è uno class per quello.

Ecco qua, impazzisci!

Un grande avvertimento anche se, quello che troverai mentre salti in cima allo stack, non sarà quello che ti aspetti. Poiché gli argomenti possono essere inseriti nei registri e non è possibile accedere ai registri dal codice gestito.

1

È possibile utilizzare MethodInfo.GetCurrentMethod().GetParameters() per ottenere un elenco di parametri del metodo. Ma è impossibile ottenere i loro valori con la riflessione.

+1

@Mehdi. Sì, il metodo GetParameters() era già trattato nella mia domanda, grazie. –

Problemi correlati