值类型使用动态推断时,在运行时对象 [英] Value types inferred as object at runtime when using dynamic
问题描述
我几乎明白为什么这个问题出现了(虽然我比欢迎外行的解释,如果你能找到的时候了!),那我敢肯定,包括装箱/拆箱我不会试图解释不正确的..
I almost understand why this particular problem arises (though I more than welcome a layman's explanation if you can find the time!), it I'm sure involves boxing/unboxing which I won't attempt to incorrectly explain..
使用我目前的知识(或缺乏)的情况,我不知道如何以最佳方式进行解决。
With my current knowledge (or lack thereof), of the situation, I'm not sure how best to proceed to resolve it.
下面是显示我的问题一个相当简化的控制台应用程序:
Here is a fairly simplified console app showing my issue:
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();
}
}
以上将输出
The above will output
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>
由于经常与这些问题的情况下,这是更好地审视它被你进入之前启动这个可怕的混乱。
As is often the case with these issues, it's better to examine where it was you started before getting into this terrible mess.
我使用起订量。我有其他的接口在我的应用程序广泛使用的通用接口。我有一个需要测试,我的界面消费者首先调用通用接口上的特定方法,要求各接口的任何方法之前的(这对我听起来像在事后一个不好的设计,我可能以后但对于解决纯学术的原因,我想首先要解决这个问题)的
I'm using Moq. I have a common interface used widely across my application by other interfaces. I have a need to test that my interface consumers first call a particular method on the common interface, before calling any methods on the various interfaces (this to me sounds like a bad design in hindsight, I may address that later but for purely academic reasons, I'd like to solve this issue first).
为了这个,我动态生成对我的每一个接口的方法,表达式 It.IsAny< T>()
参数,这些参数然后我就可以传递给 mock.Setup(generatedExpression).Callback(doSomethingCommonHere)
。
In order to this, I dynamically generate expressions for each method on my interfaces with It.IsAny<T>()
arguments, which I can then pass to a mock.Setup(generatedExpression).Callback(doSomethingCommonHere)
.
这可能是,有做,而不是我表达的建筑...?
It may well be that there is a easier way to do this instead of my expression building...?
如果没有然而,我的问题是,什么是修改我的表情建筑允许值类型的最好方法是什么?
推荐答案
这是不特定动态
。如果更换动态
与对象
您将得到同样的结果。你甚至会得到,对于实现接口的自定义值类型和你想从返回他们 Func键< IImplementedInterface方式>
结果
这样做的原因就是当你想返回一个 INT
为对象
它必须是盒装的事实 - 你猜对了。结果
的表达是用于拳击是 Expression.Convert
。并称,你的代码将解决这个异常:
This isn't specific to dynamic
. You will get the same result if you replace dynamic
with object
. You would even get that for custom value types that implement an interface and you want to return them from a Func<IImplementedInterface>
.
The reason for this is the fact that when you want to return an int
as object
it has to be boxed - as you correctly guessed.
The expression that is used for boxing is Expression.Convert
. Adding that to your code will fix the exception:
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
阵列。这是没有必要的。
BTW: As you can see, I removed the args
array. It is not necessary.
要解决与起订量,我认为你需要改变的方法有点问题。< BR>
的想法是这样的:
To work around the problem with Moq I think you need to change the approach a bit.
The idea is the following:
- 创建与调用方法的精确返回类型的表达式
- 让DLR(动态语言运行时)计算出表达式的类型
在代码:
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 });
}
用法是这样的:
Usage would be like this:
foreach(var expression in results)
{
mock.Setup((dynamic)expression);
// ...
}
这里最重要的部分是投到动态
之前传递表达设置
。
这篇关于值类型使用动态推断时,在运行时对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!