以通用表达式和动作作为参数的调用方法

时间:2018-10-16 08:26:31

标签: c# generics reflection anonymous-methods

我需要调用一个看起来像这样的方法:

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

但是TEntity仅在运行时才知道。

我知道如何调用这样的方法:

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>);

如您所见,我不知道该函数传递什么。那就是与This Question的不同之处,在其中https://github.com/tensorflow/models/blob/master/research/object_detection/object_detection_tutorial.ipynb调用了没有参数的方法。

有什么办法可以做到吗?

编辑:完整示例

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--)
        {
        }
    }
}

1 个答案:

答案 0 :(得分:0)

很遗憾,it's not possible to define generic anonymous methods

最大的问题是:您的表情/动作来自哪里?

从头开始,可能的解决方案包括将您的操作声明为单独的方法,或者使用辅助方法来创建您的操作/表达式。

// 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 });

您也可以对操作使用相同的“表达式工厂”方法:

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]);

另一种可能的解决方案包括使用Expression类的building your expression/action dynamically

// Simple(!) example
var func = Expression.Lambda(Expression.Constant(true), Expression.Parameter(classType));