以通用表达式和动作作为参数调用方法
Invoke method with generic expression and action as parameters
我需要调用如下所示的方法:
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 调用不带参数的方法的区别。
有什么办法可以做到吗?
编辑:完整示例
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--)
{
}
}
}
不幸的是it's not possible to define generic anonymous methods。
最大的问题是:你的expression/action来自哪里?
根据我的想法,可能的解决方案包括将您的操作声明为单独的方法,或者使用辅助方法来创建您的 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 });
您也可以对操作使用相同的 "expression factory" 方法:
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]);
另一种可能的解决方案包括 building your expression/action dynamically 使用 Expression
class.
// Simple(!) example
var func = Expression.Lambda(Expression.Constant(true), Expression.Parameter(classType));
我需要调用如下所示的方法:
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 调用不带参数的方法的区别。
有什么办法可以做到吗?
编辑:完整示例
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--)
{
}
}
}
不幸的是it's not possible to define generic anonymous methods。
最大的问题是:你的expression/action来自哪里?
根据我的想法,可能的解决方案包括将您的操作声明为单独的方法,或者使用辅助方法来创建您的 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 });
您也可以对操作使用相同的 "expression factory" 方法:
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]);
另一种可能的解决方案包括 building your expression/action dynamically 使用 Expression
class.
// Simple(!) example
var func = Expression.Lambda(Expression.Constant(true), Expression.Parameter(classType));