我试图了解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
答案 0 :(得分:2)
一切都是因为value
是struct
。
GCHandle.Alloc
会使用object
参数,因此如果您传递struct
,则必须将其装箱。
稍后使用
时value val = (value)gch.Target;
必须取消装箱,因此副本会存储在val
中。您之后进行的任何修改都是在副本上进行的,而不是在装箱struct
上进行的。
它与GCHandle
无关,它是值类型(struct
s)在C#中的工作方式。这就是为什么建议让值类型不可变的原因。
答案 1 :(得分:1)
因为value
是sturct
,结构不是引用类型。当你有一个结构实例和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;