我几乎理解为什么会出现这个特殊的问题(虽然如果你能找到时间,我非常欢迎外行人的解释!),我肯定会涉及拳击/拆箱,我不会试图错误地解释.. < / p>
根据我目前的知识(或缺乏)情况,我不确定如何最好地解决它。
这是一个相当简化的控制台应用程序,显示我的问题:
static void Main(string[] args)
{
try
{
// succeeds
IEnumerable<Expression<Func<TestCase1Impl, dynamic>>> results1 =
typeof(ITestCase1).GetMethods().Select(m => buildDynamicExpression(new TestCase1Impl(), m));
Console.WriteLine("{0} methods processed on ITestCase1", results1.Count().ToString());
// succeeds
IEnumerable<Expression<Func<TestCase2Impl, int>>> results2 =
typeof(ITestCase2).GetMethods().Select(m => buildTypedExpression(new TestCase2Impl(), m));
Console.WriteLine("{0} methods processed on ITestCase2", results2.Count().ToString());
// fails
IEnumerable<Expression<Func<TestCase2Impl, dynamic>>> results3 =
typeof(ITestCase2).GetMethods().Select(m => buildDynamicExpression(new TestCase2Impl(), m));
Console.WriteLine("{0} methods processed on ITestCase2", results3.Count().ToString());
}
catch (Exception ex)
{
Console.WriteLine("Failed: {0}", ex.ToString());
}
Console.ReadKey();
}
private static Expression<Func<T, dynamic>> buildDynamicExpression<T>(T arg, MethodInfo method)
{
ParameterExpression param = Expression.Parameter(typeof(T));
MethodCallExpression[] args = new MethodCallExpression[0]; // none of the methods shown take arguments
return Expression.Lambda<Func<T, dynamic>>(Expression.Call(param, method, args), new ParameterExpression[] { param });
}
private static Expression<Func<T, int>> buildTypedExpression<T>(T arg, MethodInfo method)
{
ParameterExpression param = Expression.Parameter(typeof(T));
MethodCallExpression[] args = new MethodCallExpression[0]; // none of the methods shown take arguments
return Expression.Lambda<Func<T, int>>(Expression.Call(param, method, args), new ParameterExpression[] { param });
}
public interface ITestCase1
{
string Method1();
List<int> Method2();
Program Method3();
}
public interface ITestCase2
{
int Method4();
}
private class TestCase1Impl : ITestCase1
{
public string Method1()
{
throw new NotImplementedException();
}
public List<int> Method2()
{
throw new NotImplementedException();
}
public Program Method3()
{
throw new NotImplementedException();
}
}
private class TestCase2Impl : ITestCase2
{
public int Method4()
{
throw new NotImplementedException();
}
}
以上将输出
3 methods processed on ITestCase1
1 methods processed on ITestCase2
Failed: System.ArgumentException: Expression of type 'System.Int32' cannot be used for return type 'System.Object' <irrelevant stack trace follows>
正如这些问题经常出现的情况一样,在进入这个可怕的混乱之前,最好先检查一下你的起点。
我正在使用Moq。我有一个通用接口在我的应用程序中被其他接口广泛使用。我需要测试我的接口消费者首先在公共接口上调用特定方法,然后调用各种接口上的任何方法(这对我来说听起来像是一个糟糕的设计,事后我可以解决这个问题,但是对于纯粹是学术上的原因,我想先解决这个问题)。
为此,我使用It.IsAny<T>()
参数为我的接口上的每个方法动态生成表达式,然后我可以将其传递给mock.Setup(generatedExpression).Callback(doSomethingCommonHere)
。
很可能有一种更简单的方法来代替我的表达式构建......?
如果没有,我的问题是,修改我的表达式构建以允许值类型的最佳方法是什么?
答案 0 :(得分:4)
这不是dynamic
特有的。如果将dynamic
替换为object
,您将获得相同的结果。您甚至可以获得实现接口的自定义值类型,并希望从Func<IImplementedInterface>
返回它们
这样做的原因是,当您想要将int
作为object
返回时,必须将其装箱 - 正如您正确猜到的那样。
用于装箱的表达式是Expression.Convert
。将其添加到您的代码将修复异常:
private static Expression<Func<T, dynamic>> BuildDynamicExpression<T>(
T arg,
MethodInfo method)
{
ParameterExpression param = Expression.Parameter(typeof(T));
var methodCall = Expression.Call(param, method);
var conversion =
return Expression.Lambda<Func<T, dynamic>>(
Expression.Convert(methodCall, typeof(object)),
new ParameterExpression[] { param });
}
BTW:如您所见,我删除了args
数组。没有必要。
要解决Moq的问题,我认为你需要改变一下这个方法 这个想法如下:
在代码中:
IEnumerable<Expression> results =
typeof(ITestCase2).GetMethods()
.Select(m => BuildDynamicExpression(
new TestCase2Impl(), m));
BuildDynamicExpression
看起来像这样:
private static Expression BuildDynamicExpression<T>(T arg, MethodInfo method)
{
ParameterExpression param = Expression.Parameter(typeof(T));
return Expression.Lambda(Expression.Call(param, method),
new ParameterExpression[] { param });
}
用法如下:
foreach(var expression in results)
{
mock.Setup((dynamic)expression);
// ...
}
此处的重要部分是在将表达式传递给dynamic
之前转换为Setup
。