考虑以下伪代码:
public class SomeComponent
{
private List<DisposableFoo> _foos = new List<DisposableFoo>();
public void Start()
{
for (int i = 0; i < 5; i++)
{
var foo = new DisposableFoo();
var bar = new DisposableBar(); <-- EXPLICIT DISPOSE NEEDED?
foo.SomeFunc = x => bar.DoSomethingWithX(x);
_foos.Add(foo);
}
}
public void Stop()
{
_foos.ForEach(f => f.Dispose());
}
}
基础设施是否负责处理任何捕获的IDisposable变量作为其拆除的一部分?
澄清: 我不是在询问有关管理一次性物品的最佳做法。我的问题更多的是基础设施在这种情况下做了什么。我的理解是,为了捕获变量,在基础结构中,基础结构会创建一个包含DisposableBar类型字段的类型,并且它会在&#39; bar中接收对象的引用。变量。一旦基础设施捕获了该变量,它就像是一个灰色区域&#39;对我来说,关于谁在这一点上负责确定何时不再需要变量并且可以处理它。
答案 0 :(得分:2)
简短的答案肯定是是。您需要在任何一次性物品上调用dispose。这用于清理非托管资源和依赖项。
另请注意:垃圾收集器不会调用Dispose或查找IDisposable类型。
如果它在方法中是一次性的,那么理想的是使用using
这样的语句。
public void Start()
{
for (int i = 0; i < 5; i++)
{
using (var foo = new DisposableFoo())
using (var bar = new DisposableBar())
{
foo.SomeFunc = x => bar.DoSomethingWithX(x);
_foos.Add(foo);
}
}
}
如果变量是类级别,那么你的类也应该实现IDisposable并处理它在其中使用的一次性对象。
Here是一个很好的链接,我提供了有关处理对象的更多细节。
要记住的另一件事是有时(在像C#这样的语言中)我们可以有循环依赖(这是不好的做法。)但是;它发生了很多。如果你的对象被垃圾收集并且存在循环依赖关系,它会一直挂起,直到另一个对象也被垃圾收集,这会导致进程变得难看。这一切都在幕后,对于大多数应用来说通常并不是什么大不了的事情,但要意识到这一点很重要。虽然您不应该具有循环依赖关系,但您可以在转到垃圾收集器之前实现IDisposable来清理依赖关系,从而使此过程更加清晰。 (只记得开始使用它们是不好的......那就是说,实体框架是建立在循环依赖的基础上的,所以去看看。)
另一个注意:将Dispose方法也添加到对象的析构函数中并不罕见;特别是如果对象是较低级别,单件或静态,以确保在垃圾收集期间处理一次性类型。这看起来像是:
public class SomeClass : IDisposable
{
//pretend we implement a singleton pattern here
//pretend we implement IDisposable here
~SomeClass()
{
Dispose();
}
}
<强>更新强>
要根据您的说明更新答案,我相信您会询问在丢弃一次性物品之后从一次性物品中取回的变量会发生什么。这是一个棘手的行为,在开发一次性类型时应该考虑周全。这里有一些代码显示了可能有助于您理解的类似情况的结果。 也。在开发类型时应该对此负责,但在大多数情况下,即使您处置完毕,您提供给客户的任何信息都应该留给客户。换句话说,最好不要删除,处理或操纵您在处置时允许您的类型用户检索的任何信息。
using System;
using System.Collections.Generic;
namespace Disposable_Variables_Reference
{
class Program
{
static void Main(string[] args)
{
List<string> DCNames = null;
string DCName = string.Empty;
int DCValue;
using (var disposableClass = new DisposableClass())
{
DCNames = disposableClass.Names;
DCName = disposableClass.Name;
DCValue = disposableClass.Value;
foreach (var name in DCNames) Console.WriteLine(name);
Console.WriteLine(DCName);
Console.WriteLine(DCValue);
}
foreach (var name in DCNames) Console.WriteLine(name);
Console.WriteLine(DCName);
Console.WriteLine(DCValue);
Console.Read();
}
public class DisposableClass : IDisposable
{
public List<string> Names { get; set; } = new List<string>() { "Michael", "Mark", "Luke", "John" };
public string Name { get; set; } = "Gabriel";
public int Value { get; set; } = 20;
public void Dispose()
{
Names.Clear();
Name = string.Empty;
Value = 0;
}
}
}
}
<强>输出:强>
Michael
Mark
Luke
John
Gabriel
20
Gabriel
20
名称是一个列表(引用类型),不是重写到输出。
名称是字符串(不可变引用类型), IS 重写到输出。
值为int(值类型), IS 重写为输出。
然而;如果您在Names
方法中重新分配Dispose()
,而不是清除它,那么也将被重写。示例仅包括dispose方法更改。
public void Dispose()
{
Names = null; //notice here we re-assign Names to null.
Name = string.Empty;
Value = 0;
}
新输出:
Michael
Mark
Luke
John
Gabriel
20
Michael
Mark
Luke
John
Gabriel
20
了解这种公开Names
的正确方法是将其单独留在Dispose()
方法中或公开名称,如此;返回一个新列表,以便在处理时不会删除任何参考文件。
private List<string> names = new List<string>() { "Michael", "Mark", "Luke", "John" };
public List<string> Names
{
get { return names.ToList() ; }
set { names = value; }
}
当然,这整个答案都是为了逻辑和澄清。在我给出的DisposableClass
示例中没有理由使用IDisposable。
答案 1 :(得分:0)
如果您在DisposableBar
中使用了非托管代码而不是需要处理,那么Garbage collator将负责管理托管资源。
答案 2 :(得分:-1)
是和否。正确的做法是手动处理。如果此代码与真实应用程序类似,您应该在列表中收集条形并在处理foos后处理它们。根据您的实际代码的结构,您可能需要另一种策略。如果栏是非托管资源,则应始终处置它。在大多数情况下,非托管资源包装在托管资源中,例如StreamReader包装非托管文件句柄。在这些情况下,当管理对象被垃圾收集时,假设正确实现了配置模式,将处理该对象。问题是GC不是确定性的,并且会在有内存压力时运行。可能存在这样的情况:GC无法运行,但您的应用程序缺乏文件处理程序,但由于GC只关心内存,因此它不会运行,并且不会处理非托管资源。