问题 C#4.0'dynamic'不设置ref / out参数


我正在试验 DynamicObject。我尝试做的一件事是设置值 ref/out 参数,如下面的代码所示。但是,我无法拥有的价值观 i 和 j 在 Main() 设置正确(即使它们设置正确 TryInvokeMember())。有谁知道如何打电话给 DynamicObject 对象 ref/out 参数并能够检索方法中设置的值?

class Program
{
    static void Main(string[] args)
    {
        dynamic proxy = new Proxy(new Target());
        int i = 10;
        int j = 20;
        proxy.Wrap(ref i, ref j);
        Console.WriteLine(i + ":" + j); // Print "10:20" while expect "20:10"
    }
}

class Proxy : DynamicObject
{
    private readonly Target target;

    public Proxy(Target target)
    {
        this.target = target;
    }

    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
    {
        int i = (int) args[0];
        int j = (int) args[1];
        target.Swap(ref i, ref j);
        args[0] = i;
        args[1] = j;
        result = null;
        return true;
    }
}

class Target
{
    public void Swap(ref int i, ref int j)
    {
        int tmp = i;
        i = j;
        j = tmp;
    }
}

更新7/15: 微软声称已经修复了下一版.NET的问题 http://connect.microsoft.com/VisualStudio/feedback/details/543101/net-4-0s-dynamicobject-doesn-t-set-ref-out-arguments

2012年9月8日更新: 使用VS.NET 2012与.NET 4.0和4.5进行测试,确认:它已经修复。


9371
2018-03-19 06:10


起源

完全重复: stackoverflow.com/questions/2268857/... - Gabe
@gabe:实际上我看到了这个问题,但是那个问题是关于是否可以判断一个参数是否可以通过ref / out传递,这与我在这里要求的完全不同。在我的情况下,我对此并不感兴趣,因为我可以对“目标”类进行一些反思。 - Buu Nguyen
即使你的问题不同,答案也包括你问题的答案: DynamicObject是“按值调用” - Gabe
@gabe:我仍然没有看到这样做可以证明一个完全不同的问题是另一个问题的“完全重复”,而这个问题碰巧有一个你认为可以为这两个问题分享的答案:)。 - Buu Nguyen
我尝试使用.Net 4.0的VS.Net 2010,它不起作用。 - user276648


答案:


这看起来像 可以 是一个错误 - 可能在 DynamicObject。如果你添加一个 Wrap 方法 Proxy 喜欢这个:

public void Wrap(ref int x, ref int y)
{
    target.Swap(ref x, ref y);
}

然后即使这仍然是动态调用的(即代码中的代码) Main 保持不变)代码工作...所以至少一般的“动态对象如何工作”层支持pass-by-reference。

我怀疑是否这样  确实是DLR中的一个错误,修复.NET 4可能为时已晚 - 但值得报道  无论如何它可以在服务包中修复。或者,如果这是一个故意的限制/限制,它应该在MSDN中清楚地记录(据我所知,目前不是这样)。


5
2018-03-19 06:26



有趣的解决方法,谢谢。这很好知道,但显然如果我必须实现每个转发方法,我不会求助于DynamicObject :)。我已根据您的建议向Connect提交了错误报告。 - Buu Nguyen
我不知道它是如何成为一个bug,因为它无法工作。 TryInvokeMember 获取参数的一组对象。为了将整数放入数组中,必须将它们装箱,然后复制它们。这甚至可以完成一半工作的唯一方法是,一旦调用完成,动态绑定器就会从原始args数组中复制ref args,但这只是模拟ref args。 - Gabe
@gabe:当你使用反射调用带有ref参数的方法时它是如何工作的 - 正如我所说的,当方法是静态提供的时候它仍然有效,即使它仍然存在 叫 动态。换句话说,它适用于DynamicMetaObject级别。当然,与“真实”通过引用传递相比,它会产生一些扭结(例如,变量将在方法调用期间解除关联),但对于99%的情况,我确信它无关紧要。感觉就像现在一样,忽略它们完全是ref参数这一事实会更好。 - Jon Skeet
Jon:你如何用ref参数调用InvokeMethod?如果我打电话,你是说它能按预期工作吗? Target.InvokeMember("Swap", 0, null, proxy, new object[] { i, j })? - Gabe
不,不是那样的: object[] args = new object[] { i, j }; Target.InvokeMember("Swap", 0, null, proxy, args); i = (int) args[0]; j = (int) args[1]; 换句话说,数组最后以正确的值结束,反射 - 它不是 究竟 相同的行为,但非常接近。 - Jon Skeet


这不是一个错误。正如在此处所述,DynamicObject不支持TryInvokeMember中的ref和out参数。传递给此方法的所有内容都按“值”处理。不久,TryInvokeMember方法只是忽略了这些关键字,这就是为什么你的方法不起作用的原因。

如果您遵循Jon Skeet建议并在继承自DynamicObject的类中创建自己的Wrap方法,这将是一个稍微不同的场景。 工作流程如下所示:当存在对DynamicObject的方法调用时,C#运行时绑定程序首先在类本身中查找该方法。如果可以找到一个,则调用此方法。此时,仍然保留有关“ref”和“out”参数的信息。如果它找不到这样的方法,它会调用TryInvokeMember方法并简单地抛出关于“ref”和“out”关键字的信息,并开始将每个关系视为“按值”。 请记住,DynamicObject必须支持与其他语言的互操作性,而其他语言可能没有所有C#功能。

没错,文档中现在没有关于“ref”和“out”的信息。我将把它添加到下一个文档更新中。


5
2018-03-23 19:20



好消息,谢谢!但是,虽然DynamicObject不应该为ref / out的互操作性而烦恼,但TryInvokeMember的调用者(我从你的解释中理解的是C#binder)应该打扰并尝试根据内部的args数组设置out / ref TryInvokeMember。不是吗?不应该吗? - Buu Nguyen


总而言之,DynamicObject不支持pass-by-reference,因此您不想直接进行操作。


2
2018-03-19 06:22