gpt4 book ai didi

vb.net - 将 Object 变量传递给需要 Object 参数的方法的可靠方法

转载 作者:行者123 更新时间:2023-12-04 04:47:13 24 4
gpt4 key购买 nike

引用类型的正常预期语义是它应该表现为对象标识符。如果某个变量持有对程序创建的第 5483 个对象的引用,则将该变量传递给方法应该给它一个对第 5483 个对象的引用。 VB.NET 大多是这样工作的,但有一个相当奇怪的异常(exception):即使在 Option Strict On方言,试图传递这样一个 Object 类型的变量到采用该类型参数的方法,复制一个 Object变量到另一个,或以其他方式导致 Object 类型的“右值” [借用 C 术语] 要存储到该类型的“左值”,有时会导致编译器存储对不同对象的引用。

在代码不知道也不应该关心它正在处理的对象类型的情况下,有没有什么好的方法可以避免这种情况?

如果可以让任何一个涉及的操作数的类型不是 Object ,没有问题。在泛型方法中,即使该类型恰好是 Object,其泛型类型的变量也能正常工作。 .但是,属于泛型类型的参数将被视为 Object当使用该类型调用该类型时。

考虑以下方法:

Function MakeNewWeakReference(Obj As Object) As WeakReference
Return New WeakReference(Obj)
End Function

Function GetWeakReferenceTargetOrDefault(WR as WeakReference, DefaultValue as Object) _
As Object
Dim WasTarget as Object = WR.Target
If WasTarget IsNot Nothing Then Return WasTarget
Return DefaultValue
End Function

人们会期望第一个函数会返回一个 WeakReference,只要传入的对象存在,它就会保持事件状态。人们会进一步期望,如果给第二个函数一个仍然活着的 WeakReference,该方法将返回一个引用,使其保持活着。不幸的是,如果引用引用了一个装箱的非原始值类型,那么这个假设就会失败。在这种情况下,第一个方法将返回一个对装箱值的新副本的弱引用,该副本不会被原始引用保持事件状态,第二个方法将返回一个新的装箱值副本,该副本不会保留一个在弱引用活着。

如果将方法更改为泛型:
Function MakeNewWeakReference(Of T As Class)(Obj As T) As WeakReference
Return New WeakReference(Obj)
End Function

Function GetWeakReferenceTargetOrDefault(Of T As Class)(WR as WeakReference, _
DefaultValue as T) As T
Dim WasTarget as T = TryCast(WR.Target, T)
If WasTarget IsNot Nothing Then Return WasTarget
Return DefaultValue
End Function

这将避免方法中的问题,即使要调用 MakeNewWeakReference(Of Object)GetWeakReferenceTargetOrDefault(Of Object) .不幸的是,如果要尝试使用类型为 Object 的参数的任一方法,并且被存储的东西(在第一种情况下)或它被存储到的变量(在第二种情况下)也是类型 Object ,在方法的调用或存储其返回值时仍然会出现问题。如果将自己的所有代码都放入一个泛型类中,并且只将其与 Object 类型参数一起使用,但请确保始终 TryCast类型的东西 Object到泛型类型(如果泛型类型恰好是 Object ,则此类操作实际上永远不会失败)可以解决问题,但会相当丑陋。有没有一种干净的方法来指定一个变量应该被允许持有对任何类型的堆对象的引用 Object可以,但应该始终按照所有其他引用类型的方式使用引用语义?

顺便说一句,一些可直接运行的测试代码:
Sub ObjTest(O1 As Object)
Debug.Print("Testing type {0} (value is {1})", O1.GetType, O1)
Dim O2 As Object
Dim wr As New WeakReference(O1)

O2 = O1 ' source and destination are type Object--not identity preserving

Debug.Print("Ref-equality after assignment: {0}", O2 Is O1)
Debug.Print("Ref-equality with itself: {0}", Object.ReferenceEquals(O1, O1))
GC.Collect()
Debug.Print("Weak reference still alive? {0}", wr.IsAlive)
Debug.Print("Value was {0}", O1) ' Ensure O1 is still alive
End Sub

Sub ObjTest()
ObjTest("Hey")
ObjTest(1)
ObjTest(1D)
End Sub

没有真正的理由为什么给 ObjTest(Object) 的对象类型方法应该关心它给出了什么样的对象,但是打印 true 的所有三个测试具有类似 String 的类对象或者像 Int32 这样的原始值类型使用非原始值类型失败,例如 Decimal .有什么好的方法可以解决这个问题吗?

最佳答案

(我删除了所有这部分,因为它不再适用于问题的新文本)

--- 示例代码(原始问题)

Public Class Form1

Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
Dim Obj As Object

'Argument as Object treated as String
Obj = "converting into string although still is an object"
Dim outString As String = ObjToString(Obj)

'Is, complex behaviour; equals (=), always the same
Obj = "This one is 1"
Dim is1 As Integer = IsVsEqual(Obj, False) '1
Dim equal1 As Integer = IsVsEqual(Obj, True) '1
Obj = 1.0d 'This one 2
Dim outIndex2 As Integer = IsVsEqual(Obj, False) '2
Dim equal2 As Integer = IsVsEqual(Obj, True) '1

End Sub

Private Function ObjToString(obj As Object) As String

Dim nowIWantString As String = obj.ToString()
nowIWantString = nowIWantString & " -- now string 100%"

Return nowIWantString
End Function

Private Function IsVsEqual(obj As Object, tryEqual As Boolean) As Integer

Dim obj2 As Object = obj
Dim outIndex As Integer = 0
If (tryEqual) Then
If (obj2 = obj) Then
outIndex = 1
Else
outIndex = 2
End If
Else
If (obj2 Is obj) Then
outIndex = 1
Else
outIndex = 2
End If
End If

Return outIndex
End Function

End Class

--- 回答更新的问题

我必须承认我对你展示的结果印象深刻。我从来没有详细研究过这一切,但事实上对两种不同的类型进行了两种不同的治疗;并到了挑衅的地步 ReferenceEquals(sameObject, sameObject) = False肯定是好奇。只是对您的示例的快速摘要:
Dim O1 As Object = new Object
If Not Object.ReferenceEquals(O1, O1) Then
'This object will show the "quirky behaviour"
End If

制作 Object Type经历这种情况的变量就像做 O1 = 2D 一样简单.您还观察到,在这些情况下, WeakReference必须定义略有不同: wr = New WeakReference(CType(quirkyObj, ValueType)) .

所有这一切当然很有趣(比我在阅读最后一个问题之前想到的更有趣:)),尽管可以通过依赖这样的代码(或上面的代码)来避免:
Public Function dealWithNumObjects(a As Object) As Object

Dim outObject As Object = a

If (TypeOf a Is Double) Then
'Do operations as double
ElseIf (TypeOf a Is Decimal) Then
'Do operations as decimal
ElseIf (TypeOf a Is Integer) Then
'Do operations as integer
End If

Return outObject
End Function

可以这样使用:
Dim input As Object
input = 5D 'Decimal
Dim outputDecimal As Decimal = DirectCast(dealWithNumObjects(input), Decimal)
input = 5.0 'Double
Dim outputDouble As Double = DirectCast(dealWithNumObjects(input), Double)
input = 5 'Integer
Dim outputInteger As Integer = DirectCast(dealWithNumObjects(input), Integer)

这种方法只看值,因此是否古怪并不重要( Decimal 很古怪,但 DoubleInteger 都不是,这种方法对所有这些都很好)。

总结:在阅读您的示例之前,我会说:避免问题,只需将对象用作“值的临时持有者”,尽快将它们转换为目标类型并处理目标类型。阅读您的答案后,我确实认识到您的方法似乎很可靠(“非常丑陋”?为什么?我喜欢 ReferenceEquals 方法,但如果您不喜欢它并且只想确定类型是否是原始类型,您可以依赖在 O1.GetType().IsPrimitive 上)并且可能有一些适用性。我想不出比你的例子更好的做事方法:你能够找到“古怪”的类型并保持一个弱引用。我想这是在这些条件下您可以获得的最大值。

关于vb.net - 将 Object 变量传递给需要 Object 参数的方法的可靠方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18000254/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com