Vb.net 将对象变量传递给需要对象参数的方法的可靠方法

Vb.net 将对象变量传递给需要对象参数的方法的可靠方法,vb.net,object,struct,boxing,Vb.net,Object,Struct,Boxing,引用类型的正常预期语义是它应该作为对象标识符。如果某个变量包含对程序创建的5483对象的引用,则将该变量传递给某个方法时,应为其提供对5483对象的引用。VB.NET主要是这样工作的,但有一个非常奇怪的例外:即使在对方言有严格限制的选项中,也试图将类型为Object的变量传递给采用该类型参数的方法,将一个对象变量复制到另一个,或者以其他方式导致类型为Object的“右值”[借用C术语]存储到该类型的“左值”,有时会导致编译器存储对不同对象的引用 如果代码不知道,也不应该关心它所处理的对象的类型,

引用类型的正常预期语义是它应该作为对象标识符。如果某个变量包含对程序创建的5483对象的引用,则将该变量传递给某个方法时,应为其提供对5483对象的引用。VB.NET主要是这样工作的,但有一个非常奇怪的例外:即使在对方言有严格限制的
选项中,也试图将类型为
Object
的变量传递给采用该类型参数的方法,将一个
对象
变量复制到另一个,或者以其他方式导致类型为
Object
的“右值”[借用C术语]存储到该类型的“左值”,有时会导致编译器存储对不同对象的引用

如果代码不知道,也不应该关心它所处理的对象的类型,那么有什么好的方法可以避免这种情况吗

如果所涉及的任何一个操作数的类型不是
对象
,则没有问题。在泛型方法中,其泛型类型的变量也将正常工作,即使该类型恰好是
对象
。但是,当使用该类型调用该类型

考虑以下方法:

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将在传入的对象处于活动状态时保持活动状态。我们还可以预期,如果给第二个函数一个仍然活动的WeakReference,则该方法将返回一个使其保持活动状态的引用。不幸的是,如果引用nce引用装箱的非原语值类型。在这种情况下,第一个方法将返回装箱值的新副本的弱引用,该副本不会被原始引用保持活动状态,第二个方法将返回装箱值的新副本,该副本不会使弱引用中的副本保持活动状态

如果将方法更改为通用方法:

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(对象)
GetWeakReferenceTargetOrDefault(对象)
。不幸的是,如果要尝试使用带有
Object
类型参数的任何一种方法,以及要存储的对象(在第一种情况下)或者它存储到的变量(第二个)was也是类型
Object
,在方法调用或存储其返回值时仍然会出现问题。如果将所有代码放入泛型类中,并且只与Object的类型参数一起使用,但确保始终将
TryCast
类型
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)
方法应该关心它给出了什么类型的对象,但是使用类对象(如
String
或基元值类型(如
Int32
)打印
true
的所有三个测试都会失败,而非基元值类型(如
Decimal
)会失败。有什么好办法解决这个问题吗?

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

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

---对最新问题的答复

我必须承认,你所展示的结果给我留下了深刻的印象。我从来没有详细研究过这一切,但事实上,对两组不同的类型进行了两种不同的处理;并且达到了激发
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
通过此条件生成
对象类型
变量与执行
O1=2D
一样简单。您还注意到,在这些情况下,
WeakReference
的定义必须稍有不同:
wr=New WeakReference(CType(quirkyObj,ValueType))

所有这些当然都很有趣(比我在阅读最后一个问题之前所想的要多:),尽管可以通过使用类似于以下代码(或上面的代码)来避免:

可以这样使用:

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
很奇怪,但是
Double
Integer
都不奇怪,而且这种方法可以很好地处理所有这些值)


总结:在阅读您的示例之前,我会说:避免问题,只使用对象作为“值的临时持有者”,尽快将其转换为目标类型并处理目标类型。在阅读您的答案后,我确实认识到您的方法似乎相当可靠(“相当丑陋”)?为什么?我喜欢
ReferenceEquals
方法,但是如果您不喜欢它,只想确定类型是否为基元类型,那么您可以依赖
O1.GetType().IsPrimitive
),并且可能具有一定的适用性。我想不出比您的示例更好的方法:您能够找到“古怪的”键入和以保持WeakReference。我想这是在这些条件下可以得到的最大值。

请注意,VB将调用注入到
System.Runtime.CompilerServices.RuntimeHelpers.GetObjectValue
,这正是您所注意到的:返回“如果是值类,则返回
obj
的装箱副本;否则返回
obj
本身。”

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)
Sub Assign(Of T)(ByRef lvalue As T, ByRef rvalue As T)
  lvalue = rvalue
  If Not Object.ReferenceEquals(lvalue, rvalue) Then _
    Console.WriteLine("Ref-equality lost even generically!")
End Sub