C#关于范围的内存分配/取消分配问题

C#关于范围的内存分配/取消分配问题,c#,memory-management,C#,Memory Management,我最近读了一篇关于垃圾收集的文章,决定继续下去,试图获得更多的理解。我编写了以下代码,使用语句处理,但对结果感到惊讶。。。我希望e.Parent.Name不在using块的范围内 这到底是怎么回事 static void Main(string[] args) { Employee e = new Employee(); using (Parent p = new Parent()) {

我最近读了一篇关于垃圾收集的文章,决定继续下去,试图获得更多的理解。我编写了以下代码,使用语句处理
,但对结果感到惊讶。。。我希望e.Parent.Name不在using块的范围内

这到底是怎么回事

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");
            }
        }

Dispose方法实际上对父类的实例没有任何作用,因此它仍然是一个可用的类实例

IDisposable
通常在类保留非托管资源(如数据库连接或文件)时使用,以便在调用
Dispose()
时将其清除。仅仅调用
Dispose
对非托管资源没有任何作用,方法中必须有一些代码对这些资源起作用。虽然c#可能有
使用(){}
语法将
IDisposable
对象的实例化和处置包装在try/catch/finally中,但这并不意味着它对处置的对象做了任何“特殊”的处理

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();
        }
    }
假设
Name
实际上是一个非托管资源,而不仅仅是一个字符串,您的
Dispose()
方法可以读取:

public void Dispose()
{
    Name = null;
    Console.WriteLine("Disposing Parent");
}
由于您已将
p
分配给
e.Parent
,对象本身仍然“在范围内”,因为有对它的引用,因此它仍然可以通过
Console.WriteLine(e.Parent.Name)访问从中生成输出

现在也是在的“CLR周”,本周的前3篇文章将讨论垃圾收集器及其工作/行为。它们非常值得一读:


由于e仍然存在于作用域中,因此与e(分配给e的父级)相关的任何内容都将继续存在,直到e超出作用域。

IDisposable.Dispose旨在让您使用它来清理您拥有的非托管资源(如文件句柄等),它本身不做任何事情。最常见的用法是,如果您的类具有实现IDisposable自身的成员变量,那么您现在有责任处理它们。这只是一种帮助您的模式,与垃圾收集无关—事实上恰恰相反。

Dispose方法不会从内存中销毁对象。通常,dispose方法只会释放它创建的资源。

IDisposable不是一种语言功能,在运行时没有任何特殊功能。它只是一个接口/方法,就像其他任何接口/方法一样。它恰好是一个有用的模式,因此他们添加了语法以在特定模式中自动调用该方法(
使用
),并且您可以抛出一些在名称中带有“Dispose”的特殊异常(如
ObjectDisposedException

使用块从以下位置转向:

using(SomeType t = new SomeType())
{
  t.Something();
}
变成这样:

{
  SomeType t;

  try
  {
    t = new SomeType();
    t.Something();
  }
  finally
  {
    t.Dispose();
  }
}
public class Parent : IDisposable
{
    public string Name
    {
        get
        {
            AssertNotDisposed();
            return name;
        }
        set
        {
            AssertNotDisposed();
            name = value;
        }
    }

    public void Dispose()
    {
        AssertNotDisposed();
        Console.WriteLine("Disposing Parent");
        isDisposed = true;
    }

    private void AssertNotDisposed()
    {
        if(isDisposed)
            throw new ObjectDisposedException("some message");
    }

    private string name;
    private bool isDisposed = false;
}
绝对没有办法强迫GC收集任何东西。如果堆栈中某处存在对对象的引用(忽略不安全和C++/CLI代码),或者堆栈中某个对象对对象的链接引用,则对象将处于活动状态

如果你想让代码爆炸,你可以这样做:

{
  SomeType t;

  try
  {
    t = new SomeType();
    t.Something();
  }
  finally
  {
    t.Dispose();
  }
}
public class Parent : IDisposable
{
    public string Name
    {
        get
        {
            AssertNotDisposed();
            return name;
        }
        set
        {
            AssertNotDisposed();
            name = value;
        }
    }

    public void Dispose()
    {
        AssertNotDisposed();
        Console.WriteLine("Disposing Parent");
        isDisposed = true;
    }

    private void AssertNotDisposed()
    {
        if(isDisposed)
            throw new ObjectDisposedException("some message");
    }

    private string name;
    private bool isDisposed = false;
}

如果您正在寻找另一个在尝试对已处置对象执行某些操作时会爆炸的示例

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

你可能会对陈雷蒙最近的博客文章感兴趣。啊,当然。所以,仅仅因为我把它放到了一个使用块中,就没有什么“神奇”的事情发生。这与我将该雇员传递到一个方法中,在该方法中创建父级并将其设置为雇员是一样的。在方法之外,无法完全收集父对象,因为它仍然被引用。对吗?