2012-04-11 15 views
8

Sto cercando di creare un delegato Azione genericocontrovarianza nelle espressioni

delegate void ActionPredicate<in T1, in T2>(T1 t1, T2 t2); 

e

public static ActionPredicate<T,string> GetSetterAction<T>(string fieldName) 
    { 

     ParameterExpression targetExpr = Expression.Parameter(typeof(T), "Target"); 
     MemberExpression fieldExpr = Expression.Property(targetExpr, fieldName); 
     ParameterExpression valueExpr = Expression.Parameter(typeof(string), "value"); 

     MethodCallExpression convertExpr = Expression.Call(typeof(Convert), "ChangeType", null, valueExpr, Expression.Constant(fieldExpr.Type)); 

     UnaryExpression valueCast = Expression.Convert(convertExpr, fieldExpr.Type); 
     BinaryExpression assignExpr = Expression.Assign(fieldExpr, valueCast); 
     var result = Expression.Lambda<ActionPredicate<T, string>>(assignExpr, targetExpr, valueExpr); 
     return result.Compile(); 
    } 

e qui è la mia chiamante

ActionPredicate<busBase, string> act = DelegateGenerator.GetSetterAction<busPerson>("FirstName"); 

e qui è l'oggetto di business

public abstract class busBase 
{ 

} 
public class busPerson : busBase 
{ 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    public int Age { get; set; } 

    public string GetFullName() 
    { 
     return string.Format("{0} {1}", FirstName, LastName); 
    } 
} 

e qui è l'errore che cosa ottengo durante la compilazione

Cannot implicitly convert type 'BusinessObjects.ActionPredicate<BusinessObjects.busPerson,string>' to 'BusinessObjects.ActionPredicate<BusinessObjects.busBase,string>'. An explicit conversion exists (are you missing a cast?)  

mio GetSetterAction sta tornando ActionPerdicate dove, come qui T è busPerson e sto cercando di conservarlo in ActionPredicate tenere a mente su controvarianza. Ma fallisce. non so come procedere ulteriormente. Per favore aiuto..!

+0

Hai provato la conversione esplicita? – McGarnagle

risposta

8

controvarianza generico fa non permettono di assegnare un delegato D<TDerived> ad un delegato D<TBase> a causa del motivo dimostrato di seguito (usando Action<T1> qui):

Action<string> m1 = MyMethod; //some method to call 
Action<object> m2 = m1; //compiler error - but pretend it's not. 
object obj = new object(); 

m2(obj); //runtime error - not type safe 

Come si può vedere, se ci hanno permesso di eseguiremo questo incarico, quindi interromperemo la sicurezza del tipo perché saremo in grado di provare e richiamare il delegato m1 passando l'istanza di object e non string. Andando in un altro modo, tuttavia, ovvero copiare un riferimento delegato a un tipo il cui tipo di parametro è più derivato rispetto all'origine va bene. MSDN has a more complete example of generic co/contra variance.

Pertanto vi sia bisogno di cambiare la dichiarazione di act-ActionPredicate<busPerson, string> act o, più probabilmente, non crei il metodo GetSetterAction per tornare sempre ActionPredicate<busBase, string>. Se lo fai, anche si dovrebbe aggiungere il tipo di vincolo

where T1 : busBase 

Per il metodo, e avrete anche bisogno di modificare la modalità di espressione è costruito, sostituire le prime due righe come segue:

ParameterExpression targetExpr = Expression.Parameter(typeof(busBase), "Target"); 
//generate a strongly-typed downcast to the derived type from busBase and 
//use that as the type on which the property is to be written 
MemberExpression fieldExpr = Expression.Property(
    Expression.Convert(targetExpr, typeof(T1)), fieldName); 

L'aggiunta del vincolo generico è un bel tocco per garantire che questo downcast sia sempre valido per qualsiasi T1.

Su una nota leggermente diversa: cosa c'era di sbagliato nel delegato Action<T1, T2>? Sembra fare esattamente la stessa cosa della tua? :)

+0

Come suggerito, ho provato a modificare GeetSetterAction per restituire l'azione ma ho finito con questo errore "ParameterExpression di tipo 'BusinessObjects.busPerson' non può essere utilizzato per il parametro delegato di tipo 'BusinessObjects.busBase'". Sto ottenendo questo errore su questa linea. "var result = Expression.Lambda > (assignExpr, targetExpr, valueExpr);" – kans

+0

@kans - ah sì - dovrai costruire l'albero delle espressioni in modo leggermente diverso: downcast da 'busBase' a' T1' quando si genera 'fieldExpr'. Ho aggiornato la mia risposta. –

+0

Fantastico. Molte grazie. funziona alla grande Sto creando un motore di regole basato su espressioni. Puoi buttare alcuni dei tuoi pensieri su questo .. – kans

Problemi correlati