Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Casting a member access func from Func<DerivedFromT,object> to Func<T,object>

I'm trying to creating a dynamic base mapping with fluent nhibernate.

What I'm doing is checking in by a BaseMap< T > : ClassMap< T > if for example: (typeof(ICategorizedEntity).IsAssignableFrom(typeof(T)))

If so, I wanna map a property named "Category" which belongs to ICategorizedEntity's interface, but the Map(Func) function only accepts T's properties, so I tried guessing a little with linq and came up with this:

   Expression<Func<ICategorizedEntity, object>> exp = x => x.Category;
   var parameter = Expression.Parameter(typeof (T));
   var lmd = Expression.Lambda<Func<T, object>>(exp, parameter);
   Map(lmd);

Which doesn't work, because deep inside the 'Map' function it checks the following:

   MemberExpression memberExpression = (MemberExpression) null;
   if (expression.NodeType == ExpressionType.Convert)
       memberExpression = ((UnaryExpression) expression).Operand as MemberExpression;
   else if (expression.NodeType == ExpressionType.MemberAccess)
       memberExpression = expression as MemberExpression;
   if (enforceCheck && memberExpression == null)
       throw new ArgumentException("Not a member access", "expression");

And I get the "Not a member access\r\nParameter name: expression".

How can I create and cast a MemberExpression or anything similar which will work?

like image 346
Adam Tal Avatar asked Mar 05 '26 07:03

Adam Tal


1 Answers

Func<DerivedFromT,object> represents a method that accepts a DerivedFromT parameter. Func<T,object> represents a method that accepts a T parameter. Through delegate variance introduced in C# 4, you can cast Func<T,object> to Func<DerivedFromT,object>, but not the other way round (as you’re requesting).

Think about what this means:

public class Person { }

public class Student : Person { }

public static class School
{
    public static object Greet(Person person)
    {
        return null; 
    }

    public static object Promote(Student student)
    { 
        return null; 
    }
}

In this case, the Greet method matches the delegate Func<Person,object>, whilst the Promote method matches the delegate Func<Student,object>.

Func<Person, object> greetPerson = School.Greet;
Func<Student, object> promoteStudent = School.Promote;

We can cast Greet to Func<Student,object>; if we can greet a Person, then we can also greet a Student (who is guaranteed to be a specialized form of Person).

Func<Student, object> greetStudent = greetPerson;

However, we cannot cast Promote to Func<Person,object>; although we can promote a Student, we cannot promote any Person in general, unless he/she happens to be a Student.

Func<Person, object> promotePerson = promoteStudent;   // Will not compile.

If we know that our Person is a student, we can indicate this by casting it:

Func<Person, object> promotePerson =
    personWhoIsActuallyStudent => promoteStudent((Student)personWhoIsActuallyStudent);
like image 132
Douglas Avatar answered Mar 07 '26 21:03

Douglas