2011-10-12 49 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,這將成爲通過引用傳遞屬性的明智「特徵」。 –

相關問題