5

这是我在 vb.net 中发现的一些我无法弄清楚的东西,我刚刚得到一个带有树视图的表单,然后是以下内容:

Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    treeTest.Nodes.Add("a")
    treeTest.Nodes(0).Test()
End Sub

测试是一种扩展方法:

Imports System.Runtime.CompilerServices
Public Module ExtModule
    <Extension()>
    Public Sub Test(ByRef node As TreeNode)
    End Sub
End Module

如果我使用 ByRef 那么我的树视图看起来像:

http://i.imgur.com/nQk0s.png

通过 ByVal 我得到:

http://i.imgur.com/n2ZSf.png

这似乎完全倒退了,如果我只是发送一个参考,为什么节点会出现两次,而如果我制作一个副本,它只会出现一次?

4

2 回答 2

6

好的,我已经弄清楚发生了什么。

它与扩展方法本身的关系相对较小。它更多的是关于 VB 如何处理ByRef一般情况,以及一些奇怪的行为TreeView.Nodes

特别是,如果您更改此设置,您将获得完全相同的行为:

treeTest.Nodes(0).Test()

至:

ExtModule.Test(treeTest.Nodes(0))

...即使您删除ExtensionAttribute.

下面是一些 C# 代码,它演示了相同的效果,ref根本不使用参数或扩展方法:

using System.Drawing;
using System.Windows.Forms;

class Test
{
    static void Main()
    {
        TreeView tree = new TreeView { Nodes = { "a" } };
        Form form = new Form { Controls = { tree } };
        form.Load += delegate {
            TreeNode node = tree.Nodes[0];
            tree.Nodes[0] = node;
        };
        Application.Run(form);
    }
}

重要的几行是这些:

TreeNode node = tree.Nodes[0];
tree.Nodes[0] = node;

当您的空扩展方法有一个ByRef参数时,您的代码相当于上面的 C# 代码 - 因为 VBByRef通过使用临时变量然后分配回原始属性来伪造“真实”行为。

当你的空扩展方法有一个ByVal参数时,你的代码就相当于:

TreeNode node = tree.Nodes[0];
// Do nothing

...这不会创建第二个节点。

于 2012-11-21T14:46:27.463 回答
2

我编译了一个小 VB 示例,并使用 Reflector 将其反编译为 C# 代码。这就是我得到的:

treeView.Nodes.Add("a");
TreeNodeCollection VB$t_ref$S0 = treeView.Nodes;
int VB$t_i4$S0 = 0;
TreeNode VB$t_ref$S1 = VB$t_ref$S0[VB$t_i4$S0];
ref VB$t_ref$S1.Test();
VB$t_ref$S0[VB$t_i4$S0] = VB$t_ref$S1;

它不编译。因此我做了另一个测试

treeView1.Nodes.Add("a");
treeView1.Nodes[0] = treeView1.Nodes[0];
treeView1.Nodes[0] = treeView1.Nodes[0];
treeView1.Nodes[0] = treeView1.Nodes[0];

Nodes对集合的每个分配都会在视觉上复制节点;但是,节点数仍然存在1。这显然是行为的错误TreeView

注意:显然 VB 允许通过引用扩展方法的第一个参数。这很尴尬,并且可能导致许多意想不到的行为。我的建议:不要ByRef在这里使用!

于 2012-11-21T15:09:43.593 回答