我最近浏览了一篇垃圾收集文章,决定继续玩并试图获得更多的理解。我编写了以下内容,使用using
语句进行编码,但对结果感到惊讶......我希望使用块之外的e.Parent.Name可以进行ka-blooey。
这到底发生了什么?
static void Main(string[] args)
{
Employee e = new Employee();
using (Parent p = new Parent())
{
p.Name = "Betsy";
e.Parent = p;
Console.WriteLine(e.Parent.Name);
}
Console.WriteLine(e.Parent.Name);
Console.ReadLine();
}
public class Employee
{
public Parent Parent;
}
public class Parent : IDisposable
{
public string Name;
public void Dispose()
{
Console.WriteLine("Disposing Parent");
}
}
答案 0 :(得分:10)
你的Dispose方法实际上并没有对Parent
的实例做任何事情,因此它仍然是公平的游戏/作为一个类的可用实例。
IDisposable
通常在您的类保留非托管资源(例如数据库连接或文件)时使用,以便在调用Dispose()
时清除它。只是调用Dispose
对非托管资源没有任何作用,必须是方法中的某些代码才能对这些资源执行某些操作。虽然c#可能具有using() {}
语法来在try / catch / finally中包装实例化和处理IDisposable
对象,但这并不意味着它对被处置对象执行任何“特殊”操作。
假设,假设Name
实际上是一个非托管资源,而不仅仅是一个字符串,您的Dispose()
方法可以读取:
public void Dispose()
{
Name = null;
Console.WriteLine("Disposing Parent");
}
因为您已将p
分配给e.Parent
,所以对象本身仍然处于“范围内”,因为它有对它的引用,因此Console.WriteLine(e.Parent.Name);
仍然可以从中生成输出
目前在The Old New Thing上也是“CLR周”,本周的前三篇文章讨论了垃圾收集器及其工作原理。他们非常值得一读:
答案 1 :(得分:1)
由于e仍然存在于范围内,因此在e超出范围之前,与e相关联的任何内容(指定给e的父级)仍将存在。
答案 2 :(得分:1)
IDisposable.Dispose旨在让您使用它来清理您拥有的非托管资源(如文件句柄等)。它本身没有做任何事情。最常见的用法是,如果您的类具有自己实现IDisposable的成员变量,那么您现在负责Dispose'ing它们。这只是一种帮助你的模式,与垃圾收集无关 - 事实恰恰相反。
答案 3 :(得分:1)
Dispose
方法不会从内存中销毁对象。通常,dispose方法只释放它创建的资源。
答案 4 :(得分:0)
IDisposable不是语言功能,在运行时没有什么特别之处。它只是一个与其他任何接口/方法一样的接口它恰好是一个有用的模式,因此他们添加了语法以自动调用特定模式(using
)中的方法,并且有一些特殊的例外可以抛出,在其名称中有“Dispose”(如{{1 }})。
使用块转向:
ObjectDisposedException
这样的事情:
using(SomeType t = new SomeType())
{
t.Something();
}
绝对没有办法强迫GC收集任何东西。如果在堆栈中的某个位置引用了您的对象(忽略不安全和C ++ / CLI代码),或者从堆栈中的某个对象链接引用它,那么您的对象将会存在。
如果您希望代码爆炸,您可以执行以下操作:
{
SomeType t;
try
{
t = new SomeType();
t.Something();
}
finally
{
t.Dispose();
}
}
答案 5 :(得分:0)
如果你正在寻找另一个在尝试对被处置物体做某事时会爆炸的例子。
static void Main(string[] args)
{
Employee e = new Employee();
using (Parent p = new Parent("test.txt"))
{
e.Parent = p;
using ( System.IO.StreamWriter fileWriter =
new System.IO.StreamWriter(e.Parent.File))
{
fileWriter.WriteLine("Betsy");
}
}
using (System.IO.StreamWriter fileWriter =
new System.IO.StreamWriter(e.Parent.File))
{
fileWriter.WriteLine("Betsy"); //uh-oh
}
Console.ReadLine();
}
public class Employee
{
public Parent Parent;
}
public class Parent : IDisposable
{
public System.IO.FileStream File;
public Parent(string fileName)
{
File = System.IO.File.Open(fileName, System.IO.FileMode.OpenOrCreate);
}
public void Dispose()
{
((IDisposable)File).Dispose(); //or File.Close();
}
}