2011-10-12 70 views
4

我观察到VB.net中的一个行为,在这个行为中,属性设置者被调用的次数似乎比看起来需要的多,并且调用了姐姐setter方法。为什么Property Setter会比预期更频繁地被调用?

Public Class Form1 

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load 
     Console.WriteLine("Calling WorkReferenceTypeByReference") 
     WorkReferenceTypeByReference(ReferenceTypeData) 
     Console.WriteLine("Called WorkReferenceTypeByReference") 
     Console.WriteLine("Calling WorkReferenceTypeByValue") 
     WorkReferenceTypeByValue(ReferenceTypeData) 
     Console.WriteLine("Called WorkReferenceTypeByValue") 
    End Sub 

    Public Sub WorkReferenceTypeByReference(ByRef ref As Point) 
     Dim b As Point = New Point(4, 4) + ref 
     Console.WriteLine(" adding (4,4) to " & ref.ToString) 
    End Sub 

    Public Sub WorkReferenceTypeByValue(ByVal ref As Point) 
     Dim b As Point = New Point(4, 4) + ref 
     Console.WriteLine(" adding (4,4) to " & ref.ToString) 
    End Sub 

    Private m_ReferenceType As Point = New Point(0, 0) 
    Public Property ReferenceTypeData As Point 
     Get 
      Console.WriteLine(" Calling ReferenceTypeData getter") 
      Console.WriteLine(" returning: " & m_ReferenceType.ToString) 
      Return m_ReferenceType 
     End Get 
     Set(ByVal value As Point) 
      Console.WriteLine(" Calling ReferenceTypeData setter") 
      Console.WriteLine(" value = " & value.ToString) 
      m_ReferenceType = value 
     End Set 
    End Property 
End Class 

前面的代码返回到控制台下面的输出

Calling WorkReferenceTypeByReference 
    Calling ReferenceTypeData getter 
    returning: {X=0,Y=0} 
    adding (4,4) to {X=0,Y=0} 
    Calling ReferenceTypeData setter 
    value = {X=0,Y=0} 
Called WorkReferenceTypeByReference 
Calling WorkReferenceTypeByValue 
    Calling ReferenceTypeData getter 
    returning: {X=0,Y=0} 
    adding (4,4) to {X=0,Y=0} 
Called WorkReferenceTypeByValue 

注意方法执行下列虚假调用属性setter。我认为这种行为是作为一种安全措施产生的,以防止无意中修改基础财产,尽管这可能是意图。

ByRef vs ByVal使用情况下的这种行为很容易通过选择适当的ByVal关键字来解决,然而他们最近注意到了一个更危险的行为,一个导致了重复调用的堆栈溢出,因为setter调用会更新只有调用getter的值。

Public Sub DoSomething() 
    Dim a As New CustomObject(anotherObject.AProperty(getterArgument)) 
End Sub 

Public Class AnotherObject 

    Public Property AProperty as SomeType 
     Get 
      ' Get value 
     End Get 
     Set 
      ' Set value, call DoSomething 
     End Set 
    End Property 
End Class 

在前面的例子中,主叫DoSomething的()将触发AProperty吸气剂的方法,但那时,在使用后,将火setter方法,其通过程序逻辑()再次调用DoSomething的。这是对二传手的自动调用令我困惑。

+0

使用Option Strict On编译此代码以查看问题。 –

回答

6

这实际上是VB.Net的一个特性。在你的代码中,你通过引用传递一个属性,而不是一个变量。严格来说,传递一个属性ByRef是不可能的,因为ByRef需要一个变量的引用。但是,编译器会自动为您创建临时本地并将其传递给您的方法。因为该方法可能会更改ByRef参数,该参数现在是编译器生成的临时参数,而不是您的属性,编译器会将调用插入到setter中。从本质上讲,这样的事情发生了:

Dim temp = Me.ReferenceTypeData 
Me.WorkReferenceTypeByReference(temp) 
Me.ReferenceTypeData = temp 

其他语言,如C#,不允许按引用传递属性(这样做是正确的参数传递的严格定义),而是要求你写的等效上面的代码自己。

+0

可以理解地通过引用一个方法传递getter的结果是模糊的,我想这只是令人讨厌的是,在通过引用传递的属性'完成'后,感觉需要运行setter,即使没有赋值曾经做过临时价值。有什么方法可以在编译器中将其关闭,或强制getter生成一个新的值/对象以通过引用传递,而不是属性本身? –

+0

@JCollins问题是编译器不知道是否进行了分配。 (可靠地确定是否进行了任务或修改是非常困难的,所以我不期望他们尝试。)如果您知道*方法不会做任何修改,您可以自己创建临时文件调用setter,或将方法更改为ByVal(如果适用)。我不相信有一种方法可以让编译器更改其生成的代码,或者如果您传递一个ByRef属性时会抛出错误。 –

+0

感谢您的回复,我想我简化了“赋值”,直接赋值操作符左侧的值。虽然有可能任何被调用的子程序都可能做出改变,在这种情况下,我可以看到困难。 –

1

这是一个VB.net“功能”。你不会在C#中看到这一点。当你使用ByRef时,VB.NET会复制一个对象(在这个例子中是一个指针)两次。 见http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/bc54294f-785a-467b-96ec-12d0387074e9/

“据我了解VB.NET,复制的ByRef参数值的两倍:输入法时,一次,一次从方法返回时。”

所以,在方法执行结束,它基本上是自我复制,导致调用者被调用。

也就是说,在任何对象中使用ByRef确实没有意义,它只是在使用ByVal时传递指针,所以能够修改对象的效果是相同的。 ByRef仅适用于值类型。

+0

在这种特殊情况下,ByRef是一个值类型(Double),并有一些意图。由于代码逻辑已经改变为不再需要ByRef,所以我改变了它。我已经明白,引用类型上的ByRef实际上将指针指向原始值的指针,但我不确定是否完全理解您对复制的响应。看起来,只有在对引用参数进行赋值时才能调用setter,这将成为通过引用传递属性的明智“特征”。 –

相关问题