2

我在针对 .NET 2.0 的 VS 2012 RC VB.NET 项目中遇到了一个非常奇怪的情况。由于某种原因,除了调用它的 Get 方法之外,还调用了属性的 Set 方法:

这按预期工作:

Dim _searchparray = New Byte() {37, 115, ...}
Dim rep() As Byte = _opt.ReplaceBytes
If Arrays.CompareTo(rep, _searchparray, 1, False) = -1 AndAlso _opt.SearchMatchPlaceholderInReplaceBytes Then ...

也就是_opt.ReplaceBytes 的Get 方法只被调用一次,它的Set 方法没有被调用。

但这不起作用:

Dim _searchparray = New Byte() {37, 115, ...}
If Arrays.CompareTo(_opt.ReplaceBytes, _searchparray, 1, False) = -1 AndAlso _opt.SearchMatchPlaceholderInReplaceBytes Then ...

在这里,首先调用_opt.ReplaceBytes 的Get 方法,然后Arrays.CompareTo 返回,然后调用_opt.ReplaceBytes 的Set 方法!为什么?调用堆栈表明调用者是上面示例中的最后一行!但是它在哪里设置属性?它不能在 Arrays.CompareTo 中,因为 Set 方法是在函数返回值后调用的,也不能通过 _opt.SearchMatchPlaceholderInReplaceBytes 的 Get 方法设置,因为它的 Get 方法返回的是底层字段的值,没有其他任何作用!

有人对这种奇怪的行为有解释吗?谢谢。

这是演示这一点的整个示例项目:

Imports System.Runtime.CompilerServices

Module Module1

Sub Main()
    Dim _opt As New Opts
    Dim _searchparray = New Byte() {37, 115}
    If Arrays.CompareTo(_opt.ReplaceBytes, _searchparray, 1, False) = -1 AndAlso _opt.SearchMatchPlaceholderInReplaceBytes Then
        Console.WriteLine("0")
    End If
    Console.WriteLine("1")
End Sub

End Module

Module Arrays

<Extension()> _
Friend Function CompareTo(Of T As IEquatable(Of T))(ByRef SearchArray() As T, ByRef AnotherArray() As T, ByRef aWildCardElement As T, Optional aUseWildcards As Boolean = True) As Integer
    Dim min As Integer = If(SearchArray.Length < AnotherArray.Length, SearchArray.Length, AnotherArray.Length) - 1
    If aUseWildcards AndAlso aWildCardElement IsNot Nothing Then
        For i = 0 To min
            If SearchArray(i).Equals(aWildCardElement) Then Continue For 
            If Not SearchArray(i).Equals(AnotherArray(i)) Then Return i
        Next
    Else
        For i = 0 To min
            If Not SearchArray(i).Equals(AnotherArray(i)) Then Return i
        Next
    End If
    If SearchArray.Length = AnotherArray.Length Then
        Return -1
    Else
        Return min + 1
    End If
End Function

End Module



Public Class Opts

Private _ReplaceBytes() As Byte = New Byte() {}
<Xml.Serialization.XmlIgnore()> _
Public Property ReplaceBytes As Byte()
    Get
        Return _ReplaceBytes
    End Get
    Set(ByVal value As Byte())
        _ReplaceBytes = value
    End Set
End Property

Private _SearchMatchPlaceholderInReplaceBytes As Boolean = False
Public Property SearchMatchPlaceholderInReplaceBytes() As Boolean
    Get
        Return _SearchMatchPlaceholderInReplaceBytes 'Set breakpoint here 
    End Get
    Set(ByVal value As Boolean)
        'Set breakpoint here too
        _SearchMatchPlaceholderInReplaceBytes = value
    End Set
End Property

End Class

Namespace Global.System.Runtime.CompilerServices

<AttributeUsage((AttributeTargets.Method Or (AttributeTargets.Class Or AttributeTargets.Assembly))), System.Reflection.Obfuscation(ApplyToMembers:=True, Exclude:=True)> _
Public NotInheritable Class ExtensionAttribute
    Inherits Attribute
    Public Sub New()
    End Sub
End Class

End Namespace
4

2 回答 2

3

这是 ByRef 声明和将属性作为参数传递之间的交互。这在 C# 中是被禁止的,但 VB.NET 编译器可以解决这个问题。

通过声明参数 ByRef,您可以告诉编译器您可以修改传递的对象引用。如果您将局部变量作为方法参数传递,这很好,当您的代码分配参数时,该局部变量会得到更新。但是当你传递一个属性时这是一个问题,这样的赋值必须调用属性设置器。这反过来又使传递的参数无效。这可能会导致非常难以诊断的错误。

由于可能存在错误,C# 编译器只是禁止这样做。然而,VB.NET 编译器通过确保在方法停止执行调用 setter来解决此问题。这正是您在调试器中看到的。麻烦的是,它总是调用 setter,即使你没有修改参数。

解决方法很明显,使用 ByRef 只是一个错误。您的方法实际上并未分配 SearchArray 参数。参数必须是 ByVal。

于 2012-07-30T16:40:00.697 回答
1

似乎在 VB.NET 中,当您通过引用传递 Array 类型的属性时,它会在函数末尾复制回来。这是有道理的,因为传递它 ByRef 意味着引用可能在函数内部发生了变化。由于它是通过引用传递的属性,因此我们会影响(可能已更改)对属性设置器的引用。

解决方案是通过值传递它(ByVal)。您无需在代码中通过引用传递它。事实上,在大多数情况下,通过值传递比通过引用传递更好。仅当参数也用作返回值时才使用 ByRef。

于 2012-07-30T16:18:45.613 回答