5

I'm basically trying to do this, but I don't know what T will be, so I'm building things up using Reflection and Expression trees.

// Input (I don't know about "Book")
Type itemType = typeof(Book);

// Actual Code
// Build up func p => p.AuthorName == "Jon Skeet"
ParameterExpression predParam = Expression.Parameter(itemType, "p");
Expression left = Expression.Field(predParam, itemType.GetField("AuthorName"));
Expression right = Expression.Constant("Jon Skeet", typeof(string));
Expression equality = Expression.Equal(left, right);
Delegate myDelegate = Expression.Lambda(equality, new ParameterExpression[] { predParam }).Compile(); // Not sure if I need this

// Build up predicate type (Predicate<Book>)
Type genericPredicateType = typeof(Predicate<>);
Type constructedPredicateType = genericPredicateType.MakeGenericType(new Type[] { itemType });

// I need an instance to use this predicate, right? (** This Fails **)
object predicateInstance = Activator.CreateInstance(constructedPredicateType, new object[] { myDelegate });

Basically, I have a List<Book>, which I'm trying to reflect on and Invoke its Find method. The Find method needs a Predicate<Book> instead of Func<Book, bool>, and I've been beating my head against this for a few hours.

Edit: Here is the first part of the error trace:

System.MissingMethodException: Constructor on type 'System.Predicate`1[[MyProject.Book, MyProject, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' not found.
Community
  • 1
  • 1
Jon Smock
  • 9,451
  • 10
  • 46
  • 49
  • If someone needs to see the part where I Invoke the Find method, I can also supply that, but I thought it would detract from the essential part of the question. – Jon Smock Aug 16 '10 at 16:44

2 Answers2

4

Fortunately this is pretty easy to do, just by changing your call to Expression.Lambda:

Type predicateType = typeof(Predicate<>).MakeGenericType(itemType);
LambdaExpression lambda = Expression.Lambda(predicateType, equality, predParam);
Delegate compiled = lambda.Compile();

It's not clear what you needed to do with the result, mind you... if the weakly-typed version is okay for you, that should be fine.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Did you mean to type Predicate<,> (with a comma)? – Jon Smock Aug 16 '10 at 16:54
  • I doubt it, since it only takes 1 type arg. :) – Kirk Woll Aug 16 '10 at 16:58
  • @Jon: No. I'm not sure which version of the code you saw though - it went through at least a couple of edits, partly due to incompetence and partly due to misreading the question :) – Jon Skeet Aug 16 '10 at 16:59
  • Looks like he edited it :-). Works perfectly with the single argument itemType. Thanks! Ultimately I'm (basically) passing this into List.Find(), but I'm invoking Find on a List where I don't know T. So I needed to build up the predicate this way. Thanks for all your help! – Jon Smock Aug 16 '10 at 17:00
0

Not sure if this is the same as Jon's answer:

public static Predicate<T> GetPredicate<T>()
{
    Type itemType = typeof(T);
    ParameterExpression predParam = Expression.Parameter(itemType, "p");
    Expression left = Expression.Field(predParam, itemType.GetField("AuthorName"));
    Expression right = Expression.Constant("Jon Skeet", typeof(string));
    Expression equality = Expression.Equal(left, right);
    Func<T, bool> function = (Func<T, bool>)Expression.Lambda(equality, new[] { predParam }).Compile();
    return new Predicate<T>(function);
}
Daniel Renshaw
  • 33,729
  • 8
  • 75
  • 94
  • Sorry, I tried to convey that I don't have T to supply explicitly. So instead of actually having line 1 written explicitly, Type itemType would be a parameter to your solution. – Jon Smock Aug 16 '10 at 17:02