Tracking Reference auf Object => Handle geht verloren



  • Hallo,

    ich habe ein Problem mit Tracking Referenzen, z.B. bei folgender Funktion:

    void func (String ^% d, Object ^% c)
    {
    	c = gcnew String ("new string 1");
    	d = gcnew String ("new string 2");
    }
    

    Wenn ich diese Funktion wie folgt aufrufe, ...

    String^ a = "old string 1";
    String^ b = "old string 2";
    func (a, b);
    

    ... haben nachher meine Variablen a und b folgende Werte:
    a: "new string 1"
    b: "old string 2"

    Der Parameter a verhält sich wie gewünscht, während zwischen b und d die Referenz verloren geht, wahrscheinlich durch den Cast von String auf Object.
    Ich wüsste nun gerne, was da unter der Haube genau passiert und wie man trotzdem ein call-by-reference mit Object-Typen hinbekommt.

    Bislang mache ich es statt wie oben so:

    Object^ b = "old string 2";
    

    Dann geht das Handle nicht verloren, es ist aber nicht gerade schön...

    Gruß,
    Holger



  • Guten Morgen,
    Ich finde den Fall bemerkenswert interessant und kann ihn reproduzieren. Klären kann man ihn sehr gut indem man sich den IL-Code genau anschaut:

    .method assembly static void func(string& d, object& c) cil managed
    {
        .maxstack 2
        L_0000: ldarg.1 
        L_0001: ldstr "new string 1"
        L_0006: stind.ref 
        L_0007: ldarg.0 
        L_0008: ldstr "new string 2"
        L_000d: stind.ref 
        L_000e: ret 
    }
    
    .method assembly static int32 modopt([mscorlib]System.Runtime.CompilerServices.CallConvCdecl) main() cil managed
    {
        .maxstack 3
        .locals (
            [0] string b,
            [1] string a,
            [2] int32 num,
            [3] object obj2)
        L_0000: ldc.i4.0 
        L_0001: stloc.2 
        L_0002: ldnull 
        L_0003: stloc.1 
        L_0004: ldnull 
        L_0005: stloc.0 
        L_0006: ldstr "old string 1"
        L_000b: stloc.1 
        L_000c: ldstr "old string 2"
        L_0011: stloc.0 
        L_0012: ldloc.0 
        L_0013: stloc.3 
        L_0014: ldloca.s a
        L_0016: ldloca.s obj2
        L_0018: call void <Module>::func(string&, object&)
        L_001d: ldstr "{0}\n{1}"
        L_0022: ldloc.1 
        L_0023: ldloc.0 
        L_0024: call void [mscorlib]System.Console::WriteLine(string, object, object)
        L_0029: ldc.i4.0 
        L_002a: stloc.2 
        L_002b: ldloc.2 
        L_002c: ret 
    }
    

    Meine Analyse: Bei main/L_0011 speicherst du die Referenz auf den konstanten "old string 2" in b (stloc.0), nachher wird dieselbe Referenz mit stloc.3, siehe L_0013, in einem temporären Feld gespeichert. Vor dem Aufruf der Funktion wird bei L_0016 mittels ldloca.s die Adresse des temporären Feldes auf den Stack geladen; konsequenterweise wird daher bei func/L_000d (stind.ref) auch nur das temporäre Feld überschrieben. Bei der Ausgabe der Strings wird jedoch wieder der ursprüngliche String geladen (L_0023, ldloc.0). Ich glaube nicht, dass dieses Verhalten vom Compilerhersteller gewünscht war. Es ist vermutlich ein Bug oder so, melde es doch mal ⚠

    Viel Vergnügen noch 😃


Anmelden zum Antworten