Contravariance in Expressions
c#, contravariance, covariance, expression, expression-trees
Solution
Generic contravariance does not allow you to assign a delegate `D<TDerived>` to a delegate `D<TBase>` because of the reason demonstrated below (using `Action<T1>` here):
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
As you can see, if we were allowed to do this assignment, we would then be breaking type-safety because we'd be able to try and invoke the delegate `m1` by passing and instance of `object` and not `string`. Going the other way, however, i.e. copying a delegate reference to a type whose parameter type is more derived than the source is fine. MSDN has a more complete example of generic co/contra variance.
Therefore you will either need to change the declaration of `act` to `ActionPredicate<busPerson, string> act` or, more likely, consider writing the `GetSetterAction` method to always return `ActionPredicate<busBase, string>`. If you do that, you should also add the type constraint
where T1 : busBase
To the method, and you'll also need to change how your expression is built, 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 the generic constraint is a nice touch to ensure that this downcast will always be valid for any `T1`.
On a slightly different note - what was wrong with the `Action<T1, T2>` delegate? It seems to do exactly the same thing as yours? :)
Problem
I'm trying to create a Generic 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 what i get during compilation ``` 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 is returning ActionPerdicate where as here T is busPerson and i am trying to store it in ActionPredicate keeping in mind about Contravariance. But it fails. i dont know how to proceed further. Please Help..!