2009-10-18 15 views
7

Da my recent question, provo a centralizzare il modello di dominio includendo una logica stupida nell'interfaccia di dominio. Tuttavia, ho riscontrato alcuni problemi che devono includere o escludere alcune proprietà dalla convalida.Qual è il modo migliore per definire e accedere alle proprietà selezionate in C#?

Fondamentalmente, posso usare albero delle espressioni come il seguente codice. Tuttavia, non mi piace perché ho bisogno di definire la variabile locale ("u") ogni volta che creo un'espressione lambda. Hai qualche codice sorgente più corto di me? Inoltre, ho bisogno di un metodo per accedere rapidamente alle proprietà selezionate.

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

Grazie,

risposta

9

Lambda sono grandi per molti scenari - ma se non li vogliono, forse semplicemente non li usano? Odio dirlo, ma stringhe semplici sono provate e testate, specialmente per scenari come l'associazione dati. Se si desidera un accesso rapido, è possibile esaminare HyperDescriptor oppure esistono modi per compilare un delegato per gli accessor della proprietà oppure è possibile creare uno Expression dalla stringa e compilarlo (incluso un cast su object se si desidera una firma nota, piuttosto che chiamare il (molto più lento) DynamicInvoke).

Naturalmente, nella maggior parte dei casi anche la riflessione approssimativa è abbastanza veloce e non è il collo di bottiglia.

Suggerisco di iniziare con il codice più semplice e controllare che sia effettivamente troppo lento prima di preoccuparsi che sia veloce. Se non è troppo lento, non cambiarlo. Qualsiasi delle opzioni di cui sopra avrebbe funzionato diversamente.


Un'altra riflessione; se si utilizza Expression, si potrebbe fare qualcosa di simile:

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

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

e poi prendere l'espressione a parte? Un po 'più ordinato, almeno ... ecco alcuni esempi di codice che mostra la decostruzione:

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

Una volta che conosci le MemberInfo s (me.Member in precedenza), costruire il proprio lambda per l'accesso individuale dovrebbe essere banale. Per esempio (tra cui un cast di object per ottenere una firma singola):

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

Qual è il modo migliore per mantenere le proprietà selezionate per l'accesso rapido? Lista ? –

+0

'MemberInfo' andrebbe bene; 'Func ' (o 'Func ') andrebbe bene; un 'PropertyDescriptor' andrebbe bene (specialmente con HyperDescriptor). Funzionerà in entrambi i modi ... –

1

Ecco l'espressione più breve che posso venire con:

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

Se si utilizza un tipo anonimo e un inizializzatore (consultare la mia risposta aggiornata) non è necessario il metodo dummy. Che bello –

+0

Sì, lo vedo ora :) –

Problemi correlati