VB.NET 中的函数与子程序以及 By Val 与 By Ref






3.08/5 (13投票s)
2006年3月10日
7分钟阅读

140251
VB.NET 中的函数与子程序以及 By Val 与 by Ref
引言
VB.NET 中的函数与子程序以及 By Val 与 by Ref
本文旨在为阅读此文章的 VB.net 开发者澄清关于方法的概念。
在为客户进行代码审查时,我发现有各种子程序将引用类型(如文本框、ArrayList)作为 BY Val 参数。
有一些函数没有返回值
不返回任何内容的函数。
我希望在阅读完本文后,读者能对 VB.NET 中的方法、函数和子程序有更好的理解。
方法
方法包含程序的执行语句。
方法有两种类型:子程序和函数。
在本文中,我将重点介绍子程序、函数以及 By Val 和 by ref 参数。
有关 Shared、Non-shared、External、Overridable 等其他方法类型的更多信息,请访问 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vbls7/html/vblrfvbspec7_1_6_2.asp
函数和子程序是您可以用来组织代码的最基本构建块。
理想情况下,每个函数或子程序都将执行一个明确的、逻辑性的任务。
通过将代码分解为过程,您不仅可以简化工作,还可以更轻松地将代码组织到类中,并进入面向对象编程的世界。
函数与子程序非常相似——它们的语法几乎相同,并且它们都可以执行相同的操作。然而,函数会将一个值返回给调用它的代码。
但我们大多数人到此为止。至少我是这样。
返回值并不是函数和子程序之间的唯一区别。
有趣……嗯……继续阅读。
我将使用 ILDASM 来证明 VB.NET 的子程序比 VB.NET 的函数更高效、性能更好。
我创建了一个 Vb.net ASP.net 项目。
这是我创建的两个方法(一个子程序和一个函数)
Public Sub TESTSUB ()
End Sub
Public Function TESTFUC()
End Function
对于 C# 粉丝们,这里有一个 C# 函数示例。
public void TestCSharp()
{
}
现在看看它们的 MSIL(如果您不熟悉 ILDASM 和 MSIL,请阅读 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cptools/html/cpconmsildisassemblerildasmexe.asp)
.method public instance void TESTSUB() cil managed
{
// Code size 3 (0x3)
.maxstack 8
IL_0000: nop
IL_0001: nop
IL_0002: ret
} // end of method WebForm1::TESTSUB
.method public instance object TESTFUC() cil managed
{
// Code size 3 (0x3)
.maxstack 1
.locals init ([0] object TESTFUC)
IL_0000: nop
IL_0001: ldloc.0
IL_0002: ret
} // end of method WebForm1::TESTFUC
.method public hidebysig instance void TestCSharp () cil managed
{
// Code size 2 (0x2)
.maxstack 0
IL_0000: nop
IL_0001: ret
} // end of method Class1:: TestCSharp
看看这些 MSIL 指令,C# 对 void 方法的实现是最简洁的;评估堆栈的大小为 0,除了一个强制性的 nop,然后是 return,没有其他内容。
VB.NET 子程序紧随其后,但奇怪的是它在顶部有一个 .maxstack 8 语句,他们在想什么??虽然这并不意味着运行时评估堆栈是 8,但该语句仅用于验证器在程序集加载阶段进行静态分析。如果您能帮助我理解为什么子程序有 .maxstack 8,我将不胜感激。
没有显式返回类型的 VB.NET 函数是最丑陋的。为了保持慷慨,全能的 object 被用作基类型,因此即使您返回一个由 ArrayList 组成的 ArrayList 的 ArrayList... 也没关系。
所以,我得出了以下结论(如果您觉得有任何不正确的地方,请纠正我)
1. **VB.Net 子程序比函数快**
2. **您永远不应在没有返回类型的情况下使用函数,它不会导致编译错误,但会影响性能。**
现在让我们 探索 **Byval 和 Byref**
现在让我们更仔细地看一下 By Val 和 By Ref 参数。如果您对其他参数类型感兴趣,请阅读此文
当您按 By Val 传递变量时,会创建一个变量的新实例并将其传递给被调用的例程。
对此变量值所做的任何更改都不会影响传入的原始变量的值。
如果改为传入一个 ByRef 变量,您对此变量所做的任何更改也会更改其在例程外部的值。
让我们深入一点……
在 VB 6 中,如果函数或子程序的参数没有显式地用 ByVal 或 ByRef 关键字前缀,则参数按引用传递给该例程,并且在控制权返回到调用例程后,在函数或子程序中对参数所做的修改会反映在变量的值中。
另一方面,在 VB .NET 中,如果参数中未使用 ByRef 或 By Val 关键字,则参数按值传递给例程,并且一旦控制权返回到调用程序,在函数或子程序中对参数所做的修改就会被丢弃。
关于哪个更快存在一些困惑。
一些书籍说 By Val 更好,因为 ByRef 开销更高,因为数据必须复制到子程序或函数,然后在子程序或函数完成后再复制回来。
这是不正确的。为了证明这一点,让我们再次借助 ILDASM。
Public Sub TEST_SUB_BY_VAL(ByVal i As Integer)
End Sub
Public Sub TEST_SUB_BY_REF(ByRef i As Integer)
End Sub
MSIL 如下
.method public instance void TEST_SUB_BY_VAL(int32 i) cil managed
{
// Code size 3 (0x3)
.maxstack 8
IL_0000: nop
IL_0001: nop
IL_0002: ret
} // end of method WebForm1::TEST_SUB_BY_VAL
.method public instance void TEST_SUB_BY_REF(int32& i) cil managed
{
// Code size 3 (0x3)
.maxstack 8
IL_0000: nop
IL_0001: nop
IL_0002: ret
} // end of method WebForm1::TEST_SUB_BY_REF
“ByVal 更好,因为 ByRef 开销更高,因为数据必须复制到子程序或函数,然后在子程序或函数完成后再复制回来。”这句话在 .NET V 1.0 版本中在某种程度上是正确的。
在 .NET V1.1 中,ByRef 变量的地址在调用子程序时被复制。然后子程序使用/修改内存中只有一个位置的变量。在子程序退出时,不会将 ByRef 参数复制回来。
因此,重申一下……
何时使用 BY VAL
By Val(按值)意味着被调用的例程仅将该值用于输入,以开始“单向”对话。如果 A 调用 B (ByVal x),x 成为 B 中的一个临时变量,使用 A 传递的值作为起始值。当 B 结束时,x(及其所做的任何更改)也随之结束。
ByRef(按引用)意味着该参数是对实际调用变量的引用。如果 A 调用 B (ByRef x),B 可以更改 x,并且更改会反映在 A 中。因此,MySub4 (ByRef) 会更改 g;MySub5 (ByVal) 则不会。
使用 ByVal 允许程序员将例程视为无需打开的“黑箱”。以这种方式传递的参数是“安全的”。如果您不打算在例程中更改参数的值,则应使用 ByVal。它允许您保持较小的作用域,并且在以后需要读取例程以查看其是否影响调用例程时,可以节省维护时间。
如果您需要将值发送回外部例程,请考虑使用 Function 而不是带 ByRef 的 Sub。
何时使用 ByRef?
在某些情况下,ByRef 是首选。首先,引用变量类型(例如 Forms、Textboxes 等)无法使用 ByVal 模型进行处理。ByVal t As Textbox 不会引起语法错误,但它的处理方式就好像使用了 ByRef 一样(对 t 的更改会影响实际的 textbox,而不是内存中的副本)。在这种情况下,指定 ByRef 更清晰。数组也总是被视为 ByRef。
我不是 Dotnet 大师,这是我的第一篇文章。
任何建议、更正或反馈都非常受欢迎。
谢谢并致以诚挚的问候,
Raj