2009-10-18 9 views
7

De my recent question, j'essaie de centraliser le modèle de domaine en incluant une logique stupide dans l'interface de domaine. Cependant, j'ai trouvé un problème qui doit inclure ou exclure certaines propriétés de la validation.Quelle est la meilleure façon de définir et d'accéder aux propriétés sélectionnées en C#?

Fondamentalement, je peux utiliser l'arbre d'expression comme le code suivant. Néanmoins, je ne l'aime pas car j'ai besoin de définir une variable locale ("u") chaque fois que je crée une expression lambda. Avez-vous un code source plus court que moi? De plus, j'ai besoin d'une méthode pour accéder rapidement aux propriétés sélectionnées.

public void IncludeProperties<T>(params Expression<Func<IUser,object>>[] selectedProperties) 
{ 
    // some logic to store parameter 
} 

IncludeProperties<IUser> 
(
    u => u.ID, 
    u => u.LogOnName, 
    u => u.HashedPassword 
); 

Merci,

Répondre

9

lambdas sont parfaits pour de nombreux scénarios - mais si vous ne voulez pas, peut-être tout simplement ne pas les utiliser? Je déteste le dire, mais des chaînes simples sont essayées et testées, en particulier pour des scénarios comme la liaison de données. Si vous voulez un accès rapide, vous pouvez regarder HyperDescriptor, ou il existe des moyens de compiler un délégué aux accesseurs de propriété, ou vous pouvez construire un Expression à partir de la chaîne et le compiler (y compris un cast à object si vous voulez une signature connue, plutôt que d'appeler le (beaucoup plus lent) DynamicInvoke).

Bien sûr, dans la plupart des cas, même une réflexion grossière est assez rapide et n'est pas le goulot d'étranglement.

Je suggère de commencer par le code le plus simple, et vérifiez qu'il est effectivement trop lent avant de s'inquiéter de sa rapidité. Si ce n'est pas trop lent, ne le changez pas. Chacune des options ci-dessus fonctionnerait autrement.


Autre idée; si vous utilisez Expression, vous pouvez faire quelque chose comme:

public void IncludeProperties<T>(
    Expression<Func<T,object>> selectedProperties) 
{ 
    // some logic to store parameter 
} 

IncludeProperties<IUser>(u => new { u.ID, u.LogOnName, u.HashedPassword }); 

puis prendre l'expression à part? Un peu plus net, au moins ... voici quelques exemples de code montrant la deconstruction:

public static void IncludeProperties<T>(
    Expression<Func<T, object>> selectedProperties) 
{ 
    NewExpression ne = selectedProperties.Body as NewExpression; 
    if (ne == null) throw new InvalidOperationException(
      "Object constructor expected"); 

    foreach (Expression arg in ne.Arguments) 
    { 
     MemberExpression me = arg as MemberExpression; 
     if (me == null || me.Expression != selectedProperties.Parameters[0]) 
      throw new InvalidOperationException(
       "Object constructor argument should be a direct member"); 
     Console.WriteLine("Accessing: " + me.Member.Name); 
    } 
} 
static void Main() 
{ 
    IncludeProperties<IUser>(u => new { u.ID, u.LogOnName, u.HashedPassword }); 
} 

Une fois que vous connaissez les MemberInfo s (me.Member au-dessus), la construction de vos propres lambdas pour l'accès individuel devrait être trivial. Par exemple (y compris une distribution à object pour obtenir une seule signature):

var param = Expression.Parameter(typeof(T), "x"); 
var memberAccess = Expression.MakeMemberAccess(param, me.Member); 
var body = Expression.Convert(memberAccess, typeof(object)); 
var lambda = Expression.Lambda<Func<T, object>>(body, param); 
var func = lambda.Compile(); 
+0

oooh, nice one :) –

+0

Quelle est la meilleure façon de conserver les propriétés sélectionnées pour un accès rapide? Liste ? –

+0

'MemberInfo' serait bien; 'Func ' (ou 'Func ') serait bien; un 'PropertyDescriptor' serait bien (en particulier avec HyperDescriptor). Cela fonctionnera dans les deux cas ... –

1

est ici l'expression la plus courte que je peux venir avec:

public static void IncludeProperties(Expression<Action<IUser>> selectedProperties) 
{ 
    // some logic to store parameter 
} 

public static void S(params object[] props) 
{ 
    // dummy method to get to the params syntax 
} 

[Test] 
public void ParamsTest() 
{ 
    IncludeProperties(u => S(
     u.Id, 
     u.Name 
     )); 

} 
+0

Si vous utilisez un type et un initialiseur de type anonyme (voir ma réponse mise à jour), vous n'avez pas besoin de la méthode dummy. Ce qui est bien –

+0

Oui, je le vois maintenant :) –

Questions connexes