C#关于范围的内存分配/解除分配问题

时间:2010-08-10 21:09:01

标签: c# memory-management

我最近浏览了一篇垃圾收集文章,决定继续玩并试图获得更多的理解。我编写了以下内容,使用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");
            }
        }

6 个答案:

答案 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();
        }
    }