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 メソッドは 1 回だけ呼び出され、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 メソッドが呼び出されます。なんで?コール スタックは、呼び出し元が上記のサンプルの最後の行であることを示しています。しかし、プロパティをどこに設定するのでしょうか? 関数が値を返した後に Set メソッドが呼び出されるため、Arrays.CompareTo に含めることはできず、_opt.SearchMatchPlaceholderInReplaceBytes の 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 コンパイラは、メソッドの実行が停止した後にセッターが呼び出されるようにすることで、この問題を回避します。これは、デバッガーで見たものとまったく同じです。問題は、引数を変更していなくても、常にセッターを呼び出すことです。

回避策は明らかです。ByRef の使用は単なるバグです。メソッドは実際には SearchArray 引数を割り当てません。引数は ByVal である必要があります。

于 2012-07-30T16:40:00.697 に答える
1

VB.NETでは、配列型のプロパティを参照して渡すと、関数の最後にコピーバックされるようです。ByRefを渡すことは、参照が関数内で変更された可能性があることを意味するため、これは理にかなっています。これは参照によって渡されたプロパティであるため、プロパティのセッターへの(変更された可能性のある)参照に影響を与えます。

解決策は、代わりに値(ByVal)で渡すことです。コード内で参照によって渡す必要はありません。実際、ほとんどの場合、参照よりも値で渡す方が適切です。パラメータが戻り値としても機能する場合にのみ、ByRefを使用してください。

于 2012-07-30T16:18:45.613 に答える