持续的记忆混乱

时间:2015-11-14 21:24:09

标签: c# .net memory persistence ref

我试图了解C#中的持久内存,并且不知道为什么这段代码没有保留其中一个函数的更改。

using System;
using System.Runtime.InteropServices;

public class Test2
{
    public struct value
    {
        public int sz;
    }

    public static void Main(string[] args)
    {
        one foo1 = new one(one_full);
        two foo2 = new two(two_full);
        make_calls(foo1, foo2);
    }

    public delegate void one(IntPtr ctx);
    public static void one_full(IntPtr ctx)
    {
        /* set sz and see if persists */
        GCHandle gch = GCHandle.FromIntPtr(ctx);
        value val = (value)gch.Target;

        val.sz = 6;
        Console.WriteLine("Changed sz to be 6");
    }

    public delegate void two(IntPtr ctx);
    public static void two_full(IntPtr ctx)
    {
        GCHandle gch = GCHandle.FromIntPtr(ctx);
        value val = (value)gch.Target;

        Console.Write("sz is = ");
        Console.WriteLine(val.sz);
    }

    public static void make_calls(one f_one, two f_two)
    {
        value test = new value();
        test.sz = 0;

        IntPtr ptr = GCHandle.ToIntPtr(GCHandle.Alloc(test));
        f_one(ptr);
        f_two(ptr);
    }
}

我知道它最终错过了免费但这只会导致内存管理混乱....我正在寻找是否有人可以帮助我并解释为什么sz不会留下来调用第二个函数时的值6 ...

跑步时的输出是:

Changed sz to be 6
sz is = 0

2 个答案:

答案 0 :(得分:2)

一切都是因为valuestruct

GCHandle.Alloc会使用object参数,因此如果您传递struct,则必须将其装箱。

稍后使用

value val = (value)gch.Target;

必须取消装箱,因此副本会存储在val中。您之后进行的任何修改都是在副本上进行的,而不是在装箱struct上进行的。

它与GCHandle无关,它是值类型(struct s)在C#中的工作方式。这就是为什么建议让值类型不可变的原因。

答案 1 :(得分:1)

因为valuesturct,结构不是引用类型。当你有一个结构实例和var b = instanceOfStruct时,b是一个新的结构,而不是对instanceOfStruct的引用。 b中的分组值不会反映到instanceOfStruct

在您的代码中:

value val = (value)gch.Target;

将创建value struct的新实例,其具有与gch.Target指向它的结构相同的值。更改val不会更改gch.Target后面的结构。问题是由于C#中值类型和引用类型之间的混淆。如果您将value类型更改为类而不是struct,那么您将获得所需的结果。您还可以使用dynamic来修改句柄目标的结构:

dynamic val = gch.Target;
val.sz = 6;