2

I need to call a method that looks like this:

public bool DoSomething<TEntity>(Expression<Func<TEntity, bool>> expression, Action<TEntity> action) where TEntity : class

However TEntity is only known at runtime.

I know how to Invoke the method like this:

Type classType = GetType();
MethodInfo mInfo = classType.GetMethod("DoSomething", BindingFlags.Public | BindingFlags.Instance);
MethodInfo genericMInfo = mInfo.MakeGenericMethod(GetMyType());
Object result = genericMInfo.Invoke(this, <WHAT GOES HERE>);

As you can see i don't know what to pass into the function. And thats the diffrence to This Question where a method without parameters is called.

Is there any way i can do this?

EDIT: Full Example

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace Example
{
    public class Program
    {
        static void Main(string[] args)
        {
            var example = new Example();

            // Type know at compiletime:
            example.DoSomething<MyClass>((obj) => obj.Number > 2, (obj) => obj.Number = 500);

            // Type not know until runtime. Might be MyClass, MyOtherClass or MyNextClass
            Type classType = example.GetType();
            MethodInfo mInfo = classType.GetMethod("DoSomething", BindingFlags.Public | BindingFlags.Instance);
            MethodInfo genericMInfo = mInfo.MakeGenericMethod(GetMyType());

            var result = genericMInfo.Invoke(example, new object[] { /* Expression<Func<TEntity, bool>> and Action<TEntity> */ });
            // My problem now is how to create this Expression and Action even if i know TEntity only at runtime
        }

        static Type GetMyType()
        {
            // Imagine some user-input or any other dark magic here. For the example i`ll just return a type
            return typeof(MyOtherClass);
        }
    }

    public class Example
    {
        public bool DoSomething<TEntity>(Expression<Func<TEntity, bool>> expression, Action<TEntity> action) where TEntity : MyClass
        {
            // Real code does something more useful, but its the same principle
            var myList = new List<TEntity>(GetItems<TEntity>());

            if (myList.Count > 0)
            {
                var entry = myList.AsQueryable().Where(expression).FirstOrDefault();

                if (entry != null)
                {
                    action(entry);

                    return true;
                }
            }

            return false;
        }

        private IEnumerable<T> GetItems<T>()
        {
            // Real code does something diffrent
            for (int i = 0; i < 5; i++)
            {
                yield return (T)Activator.CreateInstance(typeof(T), i);
            }
        }
    }

    public class MyClass
    {
        public MyClass(int number)
        {
            Number = number;
        }

        public int Number { get; set; }
    }

    public class MyOtherClass : MyClass
    {
        public MyOtherClass(int number)
            : base(number++)
        {
        }
    }

    public class MyNextClass : MyClass
    {
        public MyNextClass(int number)
            : base(number--)
        {
        }
    }
}
marsze
  • 15,079
  • 5
  • 45
  • 61
Max
  • 51
  • 5
  • This question is unclear. what exactly do you want to do with `expression` and `TEntity`? – Liam Oct 16 '18 at 08:27
  • @Liam I edited the question. Hopefully its clear now. I somehow need to build an Expression and Action with their exact type only known at runtime. – Max Oct 16 '18 at 08:41
  • 1
    `WHAT GOES HERE`, your parameters. How are you passing your parameters into wherever you call this? This still isn't clear. Please create a [Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve) of your problem – Liam Oct 16 '18 at 08:54
  • @Liam i attached full example. – Max Oct 16 '18 at 09:51
  • Ok, that is an awful lot of code just to replace a where clause. Also reflection is going to be inefficient. Why do you need this level of reuse for what seems like a simple block of code. I think your making this a lot more complex than it needs to be for practically zero benefit. – Liam Oct 16 '18 at 10:46
  • @Liam I don't see any other way to do this than this awful reflection stuff. Mind if you give me a hint? – Max Oct 16 '18 at 11:10

1 Answers1

0

Unfortunately it's not possible to define generic anonymous methods.

The big question is: Where do your expression/action come from?

From the top of my head, the possible solutions include declaring your action as as a separate method, or using helper methods to create your actions/expressions.

// Create a helper method to create the expression
public static Expression<Func<TEntity, bool>> MakeExpression<TEntity>()
{
    // your custom expression here
    return x => true;
}

// Declare your action as a generic method
public static void MyAction<TEntity>(TEntity input)
{
    // your action here
}

// Then you can use it like this:
var func = typeof(Example).GetMethod("MakeExpression", BindingFlags.Public | BindingFlags.Instance).MakeGenericMethod(classType).Invoke(example, new object[0]);
var action = typeof(Example).GetMethod("MyAction", BindingFlags.Public | BindingFlags.Static).MakeGenericMethod(classType ).CreateDelegate(typeof(Action<>).MakeGenericType(classType));
genericMInfo.Invoke(example, new object[] { func, action });

You could use the same "expression factory" approach for the action too:

public static Action<TEntity> MakeAction<TEntity>()
{
    // your custom action here.
    return e => { };
}

// Then:
var action = typeof(Example).GetMethod("MakeAction", BindingFlags.Public | BindingFlags.Instance).MakeGenericMethod(classType).Invoke(example, new object[0]);

Another possible solution would include building your expression/action dynamically using the Expression class.

// Simple(!) example
var func = Expression.Lambda(Expression.Constant(true), Expression.Parameter(classType));
marsze
  • 15,079
  • 5
  • 45
  • 61