这是我在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,那么我的树视图看起来像:
通过ByVal我得到:
这似乎完全是倒退,如果我只是发送一个参考,为什么节点出现两次,而如果我复制它只出现一次?
答案 0 :(得分: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#代码 - 因为VB通过使用临时变量伪造“真实”ByRef
行为,然后分配回原始财产。
当您的空扩展方法具有ByVal
参数时,您的代码只相当于:
TreeNode node = tree.Nodes[0];
// Do nothing
...并且不创建第二个节点。
答案 1 :(得分: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
!