Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在这种情况下不调用GC.KeepAlive()安全吗?_C#_.net - Fatal编程技术网

C# 在这种情况下不调用GC.KeepAlive()安全吗?

C# 在这种情况下不调用GC.KeepAlive()安全吗?,c#,.net,C#,.net,假设我们在发布版本中运行了以下代码: class SomeClass { // This field is initialized somewhere in the constructor (not shown here). public SomeOtherClass Value; ... } class SomeOtherClass { ... public void Call() { ... } } ... static void Main() {

假设我们在发布版本中运行了以下代码:

class SomeClass
{
  // This field is initialized somewhere in the constructor (not shown here).
  public SomeOtherClass Value;

  ...
}

class SomeOtherClass {
  ...
  public void Call() {
     ...
  }
}
...

static void Main()
{
  SomeClass obj = new SomeClass();
  SomeOtherMethod(obj.Value);
  ...
}

static void SomeOtherMethod(SomeOtherClass value) {
  ... // do sth else first
  value.Call();   // value is a reference that points to `obj.Value` object in heap
}
我一直看到这样的代码,当时我不熟悉GC的工作原理。 但现在我知道,垃圾收集可能会在运行时检索到obj.Value之后,即甚至在调用SomeOtherMethod()之前,收集obj。因此,有一种可能性(虽然不太可能,但仍然可能)是当
SomeOtherMethod
执行并且在操纵
obj.Value
参数之前,
obj
被标记为不可访问(因此它的成员
Value
也被标记为不可访问),并且被GC收集并重新调整内存,因此,
SomeOtherMethod
访问
是非法的

因此,我认为解决方案是将
GC.KeepAlive(obj)
GC.KeepAlive(obj.Value)
添加为:

void MyMethod()
{
  SomeClass obj = new SomeClass();
  SomeOtherMethod(obj.Value);
  ...
  GC.KeepAlive(obj);   //  or GC.KeepAlive(obj.Value);
}

但是我没有看到很多人(高级开发人员)这样做,代码总是正确运行,不会引发任何异常。那么,我的理解是否不正确,或者CLR对此有解决办法?

当读取了
obj.Value
时,该值(可能是引用或原语/结构)的值会被复制(复制到不重要的位置;注意,在引用的情况下,复制的只是引用,而不是它引用的对象)。此时,是否收集
obj
并不重要,值的副本是独立的

请注意,如果将ref返回属性或裸字段与
SomeOtherMethod(ref obj.Value)
一起使用,则情况略有不同,但在这种情况下:我们仍然知道GC可以遵循托管指针(aka
ref
),如果需要,这将使对象保持活动状态


因此,不需要
GC.KeepAlive
。您需要
GC.KeepAlive
的时间非常特殊(通常涉及终结器和数据之间的间接(非引用)关系);在大多数日常场景中:GC做你想做的事情,而不需要你的输入。

GC系统的构建是为了在无法访问对象时收集对象。你不必帮助系统进行分析
obj.Value
,一旦被读取,就不依赖于
obj
的存在。@Damien_不信者,但是
obj.Value
是指向堆中某个对象的引用,如果该对象是由GC收集的,我们如何继续调用其函数成员?您不需要像这样照看GC。如果您使用托管对象只是让它做它的工作,并假装它不存在(至少99%的时间)@amjad“但是
obj.Value
是指向堆中对象的引用”,这是一个非常微妙的说法;
obj.Value
是什么取决于您没有显示的
obj.Value
obj.Value
可以只是一个
int
;它可以是一个完全不同的对象,也可以是它本身(
返回这个;
)-但这很好:GC知道如何处理引用;我同意
ref obj.Value
可能是堆上对象内部的一个托管指针,但是:GC理解托管指针,所以同样:没有问题。正在收集的
obj
不相关,或者不会收集
obj
。“obj.Value是指向堆中某个对象的引用”-如果是,则是对其他对象的引用(无论
Value
的类型如何,如果是引用类型)。它不是对
obj
的(部分)引用。我添加了一些额外的代码来更好地解释它,如果
obj
被收集,那么
obj.Value
也被收集,那么
SomeOtherMethod
无法执行?@amjad您的编辑不会改变任何东西:一旦
obj.Value
被读取,您拥有该值的副本-您不再查看
obj
中的值;注意我确实编辑了
ref
,这可能允许您查看相同的值,但在这种情况下:您仍然不需要
GC.KeepAlive
,因为
ref
保留对象alive@amjad举个例子:你问我弗雷德的电话号码;我在手机上查了一下,然后告诉你;现在我的手机丢了/被偷了/坏了/被召回了/随便什么。这对你没有影响:你仍然有Fred的电话号码-信封背面潦草写的副本(或任何东西)甚至没有受到我的手机坏了这一事实的轻微影响。我真的很困惑,当我们传递参数时,如果参数类型是类,那么我们通过引用传递,所以不会有新的类实例作为副本创建,
SomeOtherClass
是类,而不是类struct@amjad什么是“ok”是复杂的;我必须在一般情况下编写,在一般情况下,您不知道SomeOtherClass的终结器将要做什么-例如,它可以在执行清理后重置一些关键字段,这使得它在以后从
SomeClass
的终结器访问时抛出异常,而在终结器中抛出异常意味着进程死亡;如果在终结器中使用
Thread.Sleep
,则需要对其进行拍摄。基本上,我说的是“不要在雷区跳舞”,而你却在争论“如果发生了什么”——不:停止这样做;你(和我)会弄错的,你会输的。