值类型使用动态推断时,在运行时对象 [英] Value types inferred as object at runtime when using dynamic

查看:154
本文介绍了值类型使用动态推断时,在运行时对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我几乎明白为什么这个问题出现了(虽然我比欢迎外行的解释,如果你能找到的时候了!),那我敢肯定,包括装箱/拆箱我不会试图解释不正确的..

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆