2009-02-24 4 views
7

Si consideri il seguente codice:Posso ottenere metadati specifici da un Func <T, object>?

string propertyName; 
var dateList = new List<DateTime>() { DateTime.Now }; 
propertyName = dateList.GetPropertyName(dateTimeObject => dateTimeObject.Hour); 

// I want the propertyName variable to now contain the string "Hour" 

Ecco il metodo di estensione:

public static string GetPropertyName<T>(this IList<T> list, Func<T, object> func) { 
    //TODO: would like to dynamically determine which 
    // property is being used in the func function/lambda 
} 

C'è un modo per fare questo? Ho pensato che forse questo altro metodo, usando Expression<Func<T, object>> invece di Func<T, object>, mi avrebbe dato più potere di trovare quello di cui avevo bisogno, ma non so come.

public static string GetPropertyName<T>(this IList<T> list, Expression<Func<T, object>> expr) { 
    // interrogate expr to get what I want, if possible 
} 

Questa è la prima volta che faccio qualcosa di così profondo con Linq, quindi forse mi manca qualcosa di ovvio. Fondamentalmente mi piace l'idea di passare in lambda, in modo da ottenere un controllo in fase di compilazione, ma non so che la mia idea su come posso usarli in questo caso particolare funzionerà.

Grazie

+0

possibile duplicato di [Ottieni nome e tipo di metodo utilizzando espressione lambda] (http://stackoverflow.com/questions/273941/get-method-name-and-type-using-lambda-expression) – nawfal

risposta

12

Questa è la versione che uso, restituisce un PropertyInfo, ma ottenere il nome è banale.

public static PropertyInfo GetProperty<T>(Expression<Func<T, object>> expression) 
{ 
    MemberExpression memberExpression = null; 

    if (expression.Body.NodeType == ExpressionType.Convert) 
    { 
     memberExpression = ((UnaryExpression) expression.Body).Operand as MemberExpression; 
    } 
    else if (expression.Body.NodeType == ExpressionType.MemberAccess) 
    { 
     memberExpression = expression.Body as MemberExpression; 
    } 

    if (memberExpression == null) 
    { 
     throw new ArgumentException("Not a member access", "expression"); 
    } 

    return memberExpression.Member as PropertyInfo; 
} 
+0

Fantastico, è stato perfetto. Ora il mio hack è completo! : P Ah, odio dover inventare questi stratagemmi per far funzionare le cose "come dovrebbe". Voglio dire, è divertente e tutto, ma semplicemente troppo doloroso quando c'è un vero lavoro da fare. Grazie! –

+0

Questo non funziona con i tipi nullable –

3

Qualcosa di simile dovrebbe fare il trucco:

public static string GetPropertyName<T>(this IList<T> list, Expression<Func<T, object>> expr) { 
    MemberExpression member_expression = expr.Body as MemberExpression; 
    if (member_expression == null) 
     throw new ArgumentNullException("member_expression"); 
    MemberInfo member = member_expression.Member; 
    PropertyInfo property = member as PropertyInfo; 
    string proname = memeber.name; 
} 

ATTENZIONE: Codice Air!

+0

Grazie per aver risposto a Nathan , Lo apprezzo. Eri abbastanza vicino, e dal momento che hai detto "qualcosa del genere dovrebbe fare il trucco", non avevi tecnicamente torto! Accetterò la risposta di @gcores perché la sua era corretta senza che dovessi apportare modifiche. Ti ho comunque dato un +1 per rispondere però! –

+0

C'è una piccola differenza tra le risposte di Nathan e di gcores in relazione ad alcuni casi marginali come i decimali annullabili (se ricordo bene!).Per queste proprietà il corpo dell'espressione non è un'espressione di membro ma un'espressione unaria (trattata nella risposta di gcores). –

+0

@Jason Questo è bello, avrei accettato la sua risposta così come è più completa della mia. –

6

ecco un modo molto semplice e veloce per farlo su questo blog: http://blog.bittercoder.com/PermaLink,guid,206e64d1-29ae-4362-874b-83f5b103727f.aspx

Così dato:

Func func = Name => "Valore";

È possibile ottenere il parametro lambda "Nome" dalla funzione delegata dalla chiamata:

func.Method.GetParameters() [0] .Name (restituirebbe "Nome")

Ecco il metodo Hash rivisto da Andrey:

public Dictionary<string, T> Hash<T>(params Func<string, T>[] args) 
where T : class 
{ 
    var items = new Dictionary<string, T>(); 
    foreach (var func in args) 
    { 
     var item = func(null); 
     items.Add(func.Method.GetParameters()[0].Name, item); 
    } 
    return items; 
} 

Speranza che aiuta, Patrick

2

Solo una nota a margine: func.Method.GetParameters() [0] .Name è extremelly veloce se confrontato con la compilazione del lambda ed estraendo l'espressione membro, quindi le informazioni membro, quindi il nome.