2012-03-04 14 views
6

Come ottengo il nome della proprietà di esecuzione. Se la proprietà utilizza "return" allora MethodBase.GetCurrentMethod(). Name restituisce il nome della proprietà. Ma quando uso "yield return" MethodBase.GetCurrentMethod(). Name restituisce "MoveNext". come ottengo il nome della proprietà di esecuzione quando utilizza il rendimento restituito?Come ottenere il nome della proprietà quando utilizza il rendimento restituito

codice di esempio

class Program 
    { 
     static void Main(string[] args) 
     { 

     var x = myProgram.Something; 

     Console.ReadLine(); 
     }  
    } 

public class myProgram 
{ 
    public static IEnumerable<string> Something 
    { 
     get 
     { 
      string var = MethodBase.GetCurrentMethod().Name; 
      for (int i = 0; i < 5; i++) 
      { 
       yield return var; 
      } 
     } 
    } 
} 
+4

Qual è il caso d'uso? Se capissimo la necessità e il contesto, probabilmente potremmo aiutarti meglio. – jason

+1

cosa stai cercando di ottenere? – Lloyd

+0

@Jason, potrei pensare ad alcuni casi d'uso, uno dei quali sarebbe la registrazione. È una buona domanda – kelloti

risposta

5

Come probabilmente avete notato, il compilatore riorganizza come il i metodi funzionano e Something restituisce una classe privata che implementa IEnumerable. Di conseguenza, i contenuti effettivi del metodo vengono visualizzati nel metodo MoveNext di questa classe privata, pertanto MethodBase.GetCurrentMethod non restituisce ciò che sembra debba restituire.

Accade che il nome della classe privata sia derivato dal nome del metodo originale, che in questo caso è <Enumerate>d__0. Pertanto, è possibile analizzare il nome del metodo originale da uno stack frame.

static IEnumerable<string> Enumerate() 
{ 
    var method = new StackTrace(true).GetFrame(0).GetMethod().DeclaringType.Name; 
    yield return Regex.Replace(method, @".*<([^)]+)>.*", "$1"); 
} 

static void Main(string[] args) 
{ 
    foreach (var @string in Enumerate()) 
    { 
     Console.WriteLine(@string); 
    } 
} 

Questo è, naturalmente, un hack e potrebbe facilmente non funzionare nelle future versioni di .NET

+0

Il nome non è davvero offuscato.L'offuscamento sta intenzionalmente cercando di nascondere ciò che fa il codice, cosa che non succede qui. Nomi come questo sono chiamati "indicibili", perché non sono nomi C# validi. Inoltre, non è necessario usare 'StackTrace' qui,' MethodBase.GetCurrentMethod() 'è sufficiente. – svick

+0

@svick Suppongo che tu abbia ragione, l'ho modificato io – kelloti

+0

Grazie per l'aiuto, sto andando con l'approccio stacktrace per ora sapendo che è un hack. – kumar

1

vorrei spostare l'iteratore in un metodo di supporto:

public class myProgram 
{ 
    public static IEnumerable<string> Something 
    { 
     get 
     { 
      string var = MethodBase.GetCurrentMethod().Name; 
      return GetSomethingInternal(); 
     } 
    } 

    private static IEnumerable<string> GetSomethingInternal() 
    { 
     for (int i = 0; i < 5; i++) 
     { 
      yield return i; 
     } 
    } 
} 
5

Come si può intuire, il problema qui è che una dichiarazione yield return fa un po 'di riscrittura dietro le scene, simile a come fa un'espressione using o lambda. Viene effettivamente implementato come un enumeratore, con il codice che chiama yield return come parte del metodo MoveNext sull'enumeratore.

Questo è un problema generale di utilizzare Reflection: ti dà runtime informazioni sul codice in esecuzione, che non può abbinare il vostro tempo di compilazione idea di ciò che il codice è stato.

Questo è un modo prolisso per dire che non esiste un modo semplice per ottenere le informazioni desiderate. Se dovessi spostare il yield return in un metodo separato, qualsiasi codice al di fuori di tale metodo non farebbe parte dello MoveNext, ma potrebbe non essere possibile ottenere ciò che ti serve. Non stai più ricevendo il nome del metodo che sta eseguendo il yield return, stai ricevendo il nome di esso è chiamante. Se questo è tutto ciò che interessa, sembra che questo:

public IEnumerable<string> Something 
{ 
    get 
    { 
     var x = MethodBase.GetCurrentMethod().Name; 
     return this.DoSomething(x); 
    } 
} 

private IEnumerable<string> DoSomething(string x) 
{ 
    for (int i = 0; i < 5; i++) 
    { 
     yield return x; 
    } 
} 

EDIT: mentre dubito che vi aiuterà a breve termine, per la cronaca, questo problema viene risolto anche quando si utilizza C# 5 di nuovi attributi. Dal momento che l'attributo CallerMemberName viene risolto al momento della compilazione, ea quanto pare prima che l'iteratore è stato riscritto in una classe enumeratore, produce il nome della proprietà:

public IEnumerable<string> Something 
{ 
    get 
    { 
     var x = this.GetCallerName(); 
     for (int i = 0; i < 5; i++) 
     { 
      yield return x; 
     } 
    } 
} 

private string GetCallerName([CallerMemberName] string caller = null) 
{ 
    return caller; 
} 
+0

Grazie per le informazioni terremo d'occhio CallerMemberName. a causa di altri vincoli vado con la soluzione StackTrace. – kumar