引用类型的正常预期语义是它应该表现为对象标识符。如果某个变量包含对程序创建的第 5483 个对象的引用,则将该变量传递给一个方法应该给它一个对第 5483 个对象的引用。VB.NET 大多是这样工作的,但有一个相当奇怪的例外:即使在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)
or GetWeakReferenceTargetOrDefault(Of Object)
。不幸的是,如果尝试使用带有 type 参数的任一方法Object
,并且存储的东西(在第一种情况下)或存储到的变量(在第二种情况下)也是 type 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
. 有什么好的方法可以解决这个问题吗?