Sie können GCHandleType.Weak anstelle von Pinned verwenden. Auf der anderen Seite gibt es eine andere Möglichkeit, einen Zeiger auf ein Objekt zu erhalten:
object o = new object();
TypedReference tr = __makeref(o);
IntPtr ptr = **(IntPtr**)(&tr);
Benötigt einen unsicheren Block und ist sehr, sehr gefährlich und sollte überhaupt nicht verwendet werden. ☺
Damals, als in C # keine Einheimischen mit Ref-Funktion möglich waren, gab es einen undokumentierten Mechanismus, der etwas Ähnliches erreichen konnte - __makeref.
object o = new object();
ref object r = ref o;
TypedReference tr = __makeref(o);
Es gibt einen wichtigen Unterschied darin, dass TypedReference "generisch" ist. Es kann verwendet werden, um einen Verweis auf eine Variable eines beliebigen Typs zu speichern. Für den Zugriff auf eine solche Referenz muss deren Typ angegeben werden, z. B. __refvalue(tr, object)wenn sie nicht übereinstimmt, wird eine Ausnahme ausgelöst.
Um die Typprüfung zu implementieren, muss TypedReference zwei Felder haben, eines mit der tatsächlichen Adresse der Variablen und eines mit einem Zeiger auf die Typendarstellung . Es ist einfach so, dass die Adresse das erste Feld ist.
Daher __makerefwird zuerst verwendet, um eine Referenz auf die Variable zu erhalten o. Die Umwandlung (IntPtr**)(&tr)behandelt die Struktur als ein Array (dargestellt durch einen Zeiger) von IntPtr*(Zeiger auf einen generischen Zeigertyp), auf das über einen Zeiger zugegriffen wird. Der Zeiger wird zuerst dereferenziert, um das erste Feld zu erhalten, und der Zeiger dort wird erneut dereferenziert, um den tatsächlich in der Variablen gespeicherten Wert zu erhalten o- den Zeiger auf das Objekt selbst.
Jedoch , seit 2012, ich habe eine bessere und sicherere Lösung zu kommen:
public static class ReferenceHelpers
{
public static readonly Action<object, Action<IntPtr>> GetPinnedPtr;
static ReferenceHelpers()
{
var dyn = new DynamicMethod("GetPinnedPtr", typeof(void), new[] { typeof(object), typeof(Action<IntPtr>) }, typeof(ReferenceHelpers).Module);
var il = dyn.GetILGenerator();
il.DeclareLocal(typeof(object), true);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Stloc_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Conv_I);
il.Emit(OpCodes.Call, typeof(Action<IntPtr>).GetMethod("Invoke"));
il.Emit(OpCodes.Ret);
GetPinnedPtr = (Action<object, Action<IntPtr>>)dyn.CreateDelegate(typeof(Action<object, Action<IntPtr>>));
}
}
Dadurch wird eine dynamische Methode erstellt, die zuerst das Objekt fixiert (damit sich sein Speicher nicht im verwalteten Heap verschiebt) und dann einen Delegaten ausführt, der seine Adresse erhält. Während der Ausführung des Delegaten bleibt das Objekt fixiert und kann somit sicher über den Zeiger bearbeitet werden:
object o = new object();
ReferenceHelpers.GetPinnedPtr(o, ptr => Console.WriteLine(Marshal.ReadIntPtr(ptr) == typeof(object).TypeHandle.Value));
Dies ist der einfachste Weg, ein Objekt zu pinnen , da GCHandle erfordert, dass der Typ blittbar ist, um es zu pinnen. Es hat den Vorteil, dass keine Implementierungsdetails, nicht dokumentierte Schlüsselwörter und Speicherhacking verwendet werden.