Zuverlässigen Weg, um pass Object-variable Methode, die erwartet, dass Objekt-parameter
Den normal erwarteten Semantik eines Referenz-Typ ist, dass er sich Verhalten sollte, wie eine Objekt-id. Wenn einige Variablen einen Verweis auf das 5483rd Objekt ein Programm erstellt, übergabe der variable an eine Methode geben sollte einen Verweis auf die 5483rd Objekt. VB.NET funktioniert auch meistens, wie diese, aber mit einer eher seltsamen Ausnahme: auch in der Option Strict On
Dialekt, Versuch passieren solche eine variable vom Typ Object
um eine Methode, die einen parameter von diesem Typ, kopieren Sie eine Object
Variablen zu einer anderen, oder sonst eine "rvalue" des Typs Object
[zu leihen C-Terminologie] gespeichert werden, um ein "lvalue" von dieser Art ist, wird manchmal dazu führen, dass der compiler das speichern einer Referenz auf ein anderes Objekt.
Ist es eine schöne Möglichkeit dies zu vermeiden in Fällen, in denen der code nicht kennen, und sollten sich nicht zu kümmern, über die Art der Objekte, der Umgang mit?
Wenn man entweder von den beteiligten Operanden werden von einem anderen Typ als Object
, ist es kein problem. Innerhalb eine generische Methode, Variablen des generischen Typ wird auch korrekt arbeiten, selbst wenn es das geben geschieht Object
. Parameter, die von der generische Typ, allerdings, wird so behandelt, als Object
wenn der Typ aufgerufen wird, wird die Verwendung dieses Typs.
Sollten die Methoden:
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
Würde man erwarten, dass die erste Funktion zurückkehren würde, WeakReference, dass lebendig bleibt, solange das übergebene Objekt ist. Man würde erwarten, dass, wenn die zweite Funktion ist gegeben, WeakReference, die noch am Leben ist, die Methode liefert eine Referenz, die würde lebendig zu halten. Leider ist diese Annahme schlägt fehl, wenn die Referenz bezieht sich auf eine Box mit nicht-primitiven Wert geben. In diesem Fall ist die erste Methode liefert eine schwache Referenz, um eine neue Kopie der boxed-Wert, nicht am Leben gehalten werden, indem die original-Referenz, und das zweite gibt eine neue Kopie der boxed-Wert, der wird nicht halten, die man in der schwachen Referenz lebendig.
Wenn man sich die Methoden generisch sein:
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
dass würde das problem vermeiden innerhalb der Methoden, auch wenn man zum aufrufen MakeNewWeakReference(Of Object)
oder GetWeakReferenceTargetOrDefault(Of Object)
. Leider, wenn man versucht, mit einer Methode mit einem parameter vom Typ Object
, und entweder das Ding gespeichert werden (im ersten Fall) oder die variable wird gespeichert (in der zweiten) war auch Object
würde das problem noch immer auftreten, bei der Methode invocation oder wenn die Speicherung seiner Rückkehr Wert. Wenn man alle der code in einer generischen Klasse, und immer nur verwendet es mit einem parameter vom Typ des Objekts, aber sicher immer TryCast
Dinge geben Object
zu dem generischen Typ (eine solche operation sollten eigentlich nie fehl, wenn der generische Typ passiert Object
), die funktionieren würde, um das problem zu lösen, wäre aber ziemlich hässlich. Gibt es eine saubere Möglichkeit, um anzugeben, dass eine variable erlaubt sein sollte, um einen Verweis auf jede Art von heap-Objekt den Weg Object
können, sollte aber immer so Verhalten, mit Referenz-Semantik die Art, wie alle anderen Referenz-Typen zu tun?
BTW, einige direkt ausführbarer Testcode:
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
Gibt es keinen wirklichen Grund, warum die Art von Objekt, um die ObjTest(Object)
Methode sollte darauf achten, welche Art von Objekt es gegeben, aber alle drei tests, die drucken true
mit einer Klasse-Objekt wie String
oder einen primitiven Wert geben wie Int32
scheitern mit einem nicht-primitiven Wert geben wie Decimal
. Gibt es eine schöne Möglichkeit, dass zu beheben?
Du musst angemeldet sein, um einen Kommentar abzugeben.
(Habe ich entfernt, all dies zum Teil, weil es nicht mehr anwendbar ist, um den neuen text der Frage)
--- BEISPIEL-CODE (original-Frage)
--- ANTWORT AUF DIE AKTUALISIERTE FRAGE
Muss ich erkennen, dass ich bin irgendwie beeindruckt von den Ergebnissen, die Sie zeigen. Ich habe nie sah dies alles im detail, aber die Tatsache, dass zwei verschiedene Behandlungen für zwei verschiedene Gruppen von Arten; und immer auf den Punkt provozieren
ReferenceEquals(sameObject, sameObject) = False
ist sicherlich neugierig. Nur eine kurze Zusammenfassung Ihrer Beispiel:Machen eine
Object Type
variable werde durch diesen Zustand ist so einfach wie zu tunO1 = 2D
. Sie haben auch beobachtet, dass in diesen Fällen dieWeakReference
definiert werden etwas anders:wr = New WeakReference(CType(quirkyObj, ValueType))
.All dies ist sicherlich interessant (mehr als was ich dachte vor dem Lesen dieser letzten Frage,:)), obwohl, können vermieden werden, indem man sich auf codes wie dieses (oder das oben):
Kann z.B. so verwendet werden:
Dieser Ansatz sieht nur auf Werte und so, schrulligen oder nicht, spielt eigentlich keine Rolle (
Decimal
ist gerissen aber wederDouble
nochInteger
und diese Methode funktioniert mit allen von Ihnen).In der Zusammenfassung: vor dem Lesen dein Beispiel, hätte ich gesagt: vermeiden Sie Probleme und nutzen Sie einfach die Objekte, die als "temporäre Inhaber der Werte", konvertieren Sie Sie in die Ziel-geben Sie so schnell wie möglich und befassen sich mit der target-Typ. Nach der Lektüre Ihre Antwort, die ich anerkennen, dass Ihre Methode scheint Recht solide ("ziemlich hässlich"? Warum? Ich mag die
ReferenceEquals
Ansatz, aber wenn Sie es nicht mögen, und wollen einfach nur, um zu bestimmen, wenn der Typ primitiv Sie sich verlassen könnenO1.GetType().IsPrimitive
) und hätte einige Geltung. Ich kann nicht kommen mit einem besseren Weg, Dinge zu tun, als in Ihrem Beispiel: Sie sind in der Lage, suchen Sie die "skurrile" Typen und zu halten eine WeakReference. Ich denke mal, dass das die maximale können Sie erhalten unter diesen Bedingungen.O1 = 1.0d;
sollte erstellen Sie einen neuen heap-Objekt hält eine boxedDecimal
, und speichern Sie einen Verweis auf das heap-Objekt inO1
. Es tut so. Die AussageV1 = CType(O1, ValueType);
machtV1 refer to that same object, and
O5 = V1;` machtO5
auch beziehen sich auf das gleiche Objekt. Die Aussage, die am wenigsten Entschuldigung für das anlegen eines neuen ObjektsO2 = O1;
ist die eine, die das tut. Mit derIs
Betreiber auf Referenz-Typen immer tests Referenz-Gleichheit; es kann nicht überlastet werden, etwas anderes zu tun. DieReferenceEquals
Methode nicht test-Referenz-Gleichheit...Object
, und die beiden Operanden beziehen sich beide auf die gleiche Instanz einer nicht-primitiven Wert geben. In diesem Szenario, VB6 nicht passieren Referenzen auf die Instanz, sondern erstellt für jeden Wert des TypsObject
einen neuen heap-Objekt und übergibt eine Referenz auf, das.Object
eine Bezugnahme auf die boxed nicht-primitiven Datentypen (ich habe eineDecimal
, aber alle funktionieren) und kopieren Sie die variable auf einen anderen TypObject
oder übergeben Sie es an eine Methode, die dauert eineObject
parameter. Die Abtretung oder parameter übergeben, wird der Betrieb kopieren oder übergeben Sie einen Verweis auf ein neues Objekt-Instanz mit den gleichen Daten. Anderen Referenz-Typen weisen nicht ein solches Verhalten.Variant
funktioniert; ich erwähne es, weil VB6 zu VB.NET tools verwendenObject
als Ersatz fürVariant
und ich vermute, VB.NET möglicherweise werden versuchen, machen es zu handeln, wie ein VB6 -Variant
zu handeln vermag, wenn man zuordnen könnte eine Struktur geben (die kann man nicht in VB6).Obj
TypObject
, ich möchte eine Methode aufrufen, die einen parameter vom TypObject
, und ich will, dass die Methode zum erhalten einer Referenz auf das eigentliche Objekt verweistObj
. Gibt es eine Möglichkeit, ich kann das tun, ohne anderen code für die where -Obj
bezieht sich auf eine nicht-primitive value-type-Objekt als für den Fall, woObj
bezieht sich auf ein class-Objekt?Is
, sondern eher aus dem Verhalten des ersten abtretungserklärung. DieIs
Betreiber arbeiten 100% korrekt zu berichten, obobj
undobj2
auf das gleiche Objekt beziehen. Das problem ist, dass es schwer ist, eine routine, akzeptiert alleObject
zu speichern oder zu übergeben, um das empfangene Objekt in einer Weise, bewahrt seine Identität.=
Betreiber ist nicht verfügbar aufObject
imOption Strict On
Dialekt vb.net (dieOption Strict Off
Dialekt hat genug Macken, dass es sollte nie verwendet werden, für jeden Zweck, aber die Portierung von VB6-code). WennO1
ist einObject
die hat einen boxedDecimal
,Object.Equals(O1,O1)
etwa 50-mal so langsam wie es wäre, wennO1
waren alle anderen den Typ der Referenz.Object.Equals(TryCast(O1,ValueType),TryCast(O1,ValueType))
ist nur etwa doppelt so langsam, wie es sein sollte, aber es ist hässlich und wird scheitern, wennO1
ist nicht ein Stand-alone-Wert-Typ.Object
unvermeidbar ist, ach, da kann man müssen es erlauben, beliebige Arten von Informationen weitergegeben werden, durch Methoden. Zum Beispiel, ein Datenbank-wrapper-Methode können Sie ein Feld-name string und eineObject
repräsentiert den Inhalt des Feldes. DassObject
halten eine numerische primitive, eine Struktur, die wieDateTime
eineString
eineByte[]
oder wer weiß was sonst noch. Der wrapper könnte-Dokument, dass, wenn die Datenbank gibt einen Wert vom Typ Instanz, wird es wieder ein Verweis auf eine andere Instanz, die das gleiche Daten; die meisten client-code würde nicht solche Sachen interessieren...Object
und erwartet, um ihn mit Framework-Methoden, die auch die Verwendung von typeObject
. Solche Methoden sollten nicht wissen, noch kümmern die Arten von Instanzen durch die gezielte übergebenObject
Referenzen, aber ich habe nicht gefunden, einen schönen Typ-unabhängige Art und Weise etwas zu machen, konstruieren Sie eine schwache Referenz auf das aktuelle Objekt-Instanz übergeben wurde.Object
als der Typ. Das funktioniert, sorta, aber man hat zu nutzenCType
oderTryCast
alle die Zeit, wenn Sie versuchen zu speichernObject
in eine variable von diesem Typ. Weiter, wenn eine Methode nimmt einen parameter des generischen Typs, Aufruf der Methode mitObject
als der generische Typ heißt, seine Parameter werden als TypObject
, unterliegen dem seltsamen Verhalten. Ich Frage mich, ob beliebiger Produktions-code stützt sich auf dieses Verhalten?Option Strict On
code überall in der Welt, stützt sich auf die Skurrilität ist, die Dinge, deren einziger Zweck ist, zu zeigen und zu meckern, ich Frage mich, ob es würde jeder Grund für MS nicht zu beseitigen es in zukünftigen Versionen von VS (oder zumindest eine konfigurierbare option, dies zu tun). Ich bin mir ziemlich sicher, dass wir mit der unheimlichen machto2 = o1
langsamer als Sie es sonst wäre, auch wenn es bewirkt, dass kein anderes problem, also, wenn Sie nichts braucht, das seltsame Verhalten der Beseitigung es wäre eine Reine "win".Object
um eine Methode, die dauert eineObject
nicht immer übergeben Sie die Referenz, aber ich habe charakterisiert, die das genaue Verhalten genauer als das, und ich würde denken, dass eine solche Charakterisierung würde nützliche Informationen.Beachten Sie, dass die VB injiziert Aufrufe
System.Runtime.CompilerServices.RuntimeHelpers.GetObjectValue
die dokumentiert genau das tun, was Sie merken: wieder eine "boxed kopieren vonobj
wenn es ist ein Wert der Klasse; sonstobj
selbst zurückgegeben."In der Tat die Dokumentation geht auf zu sagen, wenn der Werttyp ist unveränderlich, es gibt das gleiche Objekt zurück übergeben. (Es nicht zu erklären, wie der Unveränderlichkeit ist bestimmt, und es ist ein
InternalCall
🙁 )Scheint es
Decimal
,Date
, und, natürlich, einen Benutzer-definiertenStructure
werden als veränderlich, die von der CLR.Tatsächlich versuchen, Ihre Frage zu beantworten: VB.NET nicht nennen
GetObjectValue
, sondern nutzt direkt die MSILbox
Befehl bei der Verwendung eines generischen Typs:Dies NICHT alles schreiben, was für die Typen die ich ausprobiert habe, aber
GetObjectValue
heißt die callsite 🙁(BTW Dies ist ein Fall, wo
ReferenceEquals
ist verfügbar, wennIs
ist nicht).Die Kommentare aus der reference source:
Ich habe keine weiteren Bemerkungen zu machen, die in dieser Phase-nur eine Konsolidierung in einem Ort.
Hatte ich diesen link gespeichert in meine Chrome-Lesezeichen: RuntimeHelpers.GetObjectValue warum. Ich kann mich nicht erinnern, wie ich vor langer Zeit abgelegt haben.
Object.ReferenceEquals(someObject, someValueType)
die können nie alle nützlichen Sinn würde sich weigern, zu kompilieren].Object
im code kompiliert mitOption Strict On
. Wenn es eine Art vertreten, die "mit dem Wert von jeder Art", der compiler erlaubt späte Bindung mit dieser Art auch inOption Strict On
- Modus, und man könnte auch vermeiden, die unerwünschte kopieren von einer Besetzung oder Zwang zuSystem.Object
, dann ist die implizite kopieren könnte hilfreich sein. DaOption Strict On
nicht zulassen, dass jede späte Bindung, es ist jedoch unklar, welche Art von mutation VB.NET könnte sein, Versuch Schutz gegen.Is
angewendet werden, um zwei AllgemeineTs
. (Es erlaubtIs Nothing
.)Is
des Betreibers eines Referenz-Typ, und nicht bereit ist, seine box einen Wert geben, es zu bekommen; es ist schade, es gibt keinen Weg, um eine andere Methode, die parameter Verhalten sich wie, dass. BTW,Is
akzeptieren-Klasse-eingeschränkte Generika.int[]
können Kapseln einen Wert in zwei Arten: es halten kann die einzige Referenz, überall im Universum, zu einem array, oder es kann eine gemeinsam nutzbare Referenz auf ein array, dass die sich nie ändern (vorausgesetzt, jeder mit einem Verweis stimmt nicht, es zu teilen mit jedem code, ändern). In den meisten Fällen entweder der Inhaber einer Referenz, entweder wissen, ob es kapselt eine nicht freigegebene änderbarer Wert, eine gemeinsame, unveränderliche Wert, oder ein Unternehmen, der Identität, oder der Halter wird aus einer Sammlung, deren Schöpfer wusste, dass.ValueType
, dannGetObjectValue
ist nicht genannt, wie erwähnt, hier.ValueType
vor der übergabe an etwas, das erwartetObject
vermeiden Sie das zeitraubende und semantisch fehlerhaften Kopiervorgang, aber was für einer Methode akzeptiert einObject
? Zum Beispiel, wenn das, was ich will, istSub DoTwice(Object O) : DoIt(O) : DoIt(O) : End Sub
soll ich wirklich haben, um es zu schreiben als...Sub DoTwice(Object O) : Dim V = TryCast(O, ValueType) : If V IsNot Nothing Then DoIt(V) : DoIt(V): Else DoIt(O) : DoIt(O) : End If : End Sub
? Mit zu tun, ein extra-Typ-check zur Laufzeit für den alleinigen Zweck der Verhinderung eine Besondere-case-Verhalten durch den compiler, die, so weit ich erzählen kann, immer schädlichStrict On
Modus [kann jemand identifizieren, einen einzigen Fall, wo das Verhalten ist tatsächlich hilfreich imStrict On
code]?