Contravariance in Expressions

I am trying to create a general action delegate

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

and

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

and here is my caller

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

and here is the business object

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

and here is the error I get at compile time

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

My GetSetterAction returns an ActionPerdicate, where T is here busPerson, and I'm trying to save it in an ActionPredicate, referring to the Contravariance. But that fails. I do not know how to move on. Please, help..!

+8
c # covariance expression contravariance expression-trees
source share
1 answer

General contravariance does not allow delegate D<TDerived> delegate D<TBase> due to the reason shown below (using Action<T1> here):

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

As you can see, if we were allowed to perform this task, we would then violate type safety, because we could try to call the delegate m1 by passing and an instance of object , not string . In other words, for example, copying a delegate reference to a type whose parameter type is displayed more than the source. MSDN has a more complete example of a common dispersion of co / contra .

Therefore, you need to either change the act declaration to ActionPredicate<busPerson, string> act , or rather consider using the GetSetterAction method to return ActionPredicate<busBase, string> . If you do this, you should also add a type constraint

 where T1 : busBase 

In the method, and you will also need to change the way you create the expression, replace the first two lines as follows:

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

Adding a general restriction is a pleasant touch, ensuring that this downcast is always valid for any T1 .

In a slightly different note - what was wrong with the Action<T1, T2> delegate? It seems he is doing the same as yours? :)

+8
source share

All Articles