Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call a method with instance using Expression

Tags:

c#

For example I have some class with some property:

public class SomeClass
{
    public Version Version { get; set; }
}

And I have a list of this type with sample data:

var list = new List<SomeClass>();

for (var i = 0; i < 1000; i++)
{
    list.Add(new SomeClass
    {
        Version = new Version(i, i / 2, i / 3, i / 4),
    });
}

I want to write method that filters by version using Version.Equals method:

var filterValue = new Version(12, 6, 4, 3);

var modelType = typeof(SomeClass);
var propertyType = typeof(Version);
var arg = Expression.Parameter(modelType, "x");
var property = Expression.Property(arg, "Version");
var value = Expression.Convert(Expression.Constant(filterValue), propertyType);
var versionEqualsMethod = typeof(Version).GetMethod("Equals", new[] { typeof(Version) });

/////////
Expression inst = null; // <-- ???
/////////

var expr = Expression.Call(inst, versionEqualsMethod, property, value);

var delegateType = typeof(Func<,>).MakeGenericType(modelType, typeof(bool));
var delegateValue = Expression.Lambda(delegateType, expr, arg).Compile();

var genericMethod =
    typeof(Enumerable).GetMethods()
        .First(
            method =>
                method.Name == "Where" && method.IsGenericMethodDefinition
                && method.GetGenericArguments().Length == 1 && method.GetParameters().Length == 2)
        .MakeGenericMethod(modelType);

var result = genericMethod.Invoke(null, new object[] { list, delegateValue });

What do I use as instance in Expression.Call?

UPDATE

Solution is:

var expr = Expression.Call(property, versionEqualsMethod, value);
like image 784
Vitone Avatar asked Apr 21 '26 05:04

Vitone


1 Answers

You normally would do:

var filterValue = new Version(12, 6, 4, 3);

var modelType = typeof(SomeClass);
var propertyType = typeof(Version);
var arg = Expression.Parameter(modelType, "x");
var property = Expression.Property(arg, "Version");

// Changes from here onward
var value = Expression.Constant(filterValue);
var versionEqualsMethod = typeof(Version).GetMethod("Equals", new[] { typeof(Version) });
var expr = Expression.Call(property, versionEqualsMethod, value);

Because the Equals would be used like:

model.Version.Equals(filterValue);

I'm not handling the model.Version == null case!

Note that you don't need the Expression.Convert.

And what you are doing is ok if the "containing method" (the method where you'll put this code) is non-generic, but normally it would be a generic method, that has as the generic parameter the modelType, so the last part of the code would be different (starting from var delegateType =), because you could use the TModelType generic type directly.

like image 178
xanatos Avatar answered Apr 22 '26 18:04

xanatos



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!