我来自C ++背景,并希望在下面的问题陈述中得到C#(.NET)专家的一些想法,我对解决方案方法持开放态度,但要求被冻结。
问题陈述:
让系统在删除拥有对象后立即自动清理从属对象(与GC提供的内容略有不同。)
从属对象可能具有除其拥有对象之外的其他引用,但只要删除拥有对象,依赖对象就需要进行
能够用存根对象(占位符)引用替换其他未完成的引用作为实际对象不再退出
系统需要与对象无关,并且应该能够检测引用或将其替换为从System.Object继承的任何对象(.net)
术语定义:
依赖对象:始终需要所有者的对象,但也可以由其他对象引用。然而,依赖对象的生命周期将由拥有对象 完全 拥有。如果删除了拥有对象,则必须删除依赖对象。
存根对象这些是表示已删除的引用的对象。
功能背景
为了能够支持功能需求,我们需要一个系统,它将自动清理所有者被删除的依赖对象,然后它将用存根替换其他引用,以指示它所持有的对象具有已被删除或卸载,
用一个简单的例子解释这个
时间T1 - 假设我们创建了一个Line对象。由于创建一条线需要一个起点和终点,因此它创建了2个Point(Pt1和Pt2)对象。 Point对象标记为Dependent对象,Line Object是Owner。因此,在任何时候,如果我们删除Line,它应该去删除Pt1和Pt2。
时间T2:我们创建两个新点Pt3和Pt4(这些是现在的独立对象)
时间T3:我们创建一个参考的曲线对象(Pt2,Pt3和Pt4)。在这里,Pt2的生命周期由Line对象控制。
时间T4:我们从图形中删除Line对象,现在作为一项要求,此操作必须去除并删除Pt1和Pt2,因为它们是由Line和Line对象创建的。
时间T5:由于曲线也引用了Pt2,因此现在它的几何计算是不完整的,并且将被引用到存根对象。曲线对象将被标记为已损坏,以便在将来的时间点我们可以编辑它以引用新点。
拥有这个系统的关键问题是因为删除是由.NET系统控制的,所以我们无法控制它。任何想过如何在C#或.NET中实现这一点(在C ++中我们完全控制内存管理,因此可以在删除指针之前确定指针的活动引用,并在内存中删除或替换它们。)
我理解垃圾收集器有其巨大的好处,但这也是我们需要在基于.NET的C#模型中支持的关键要求。
任何想法,建议都表示赞赏。
答案 0 :(得分:3)
通常,您无法控制C#中的内存释放。正如Ameya所建议的那样,你能做的就是有一个“肮脏”的旗帜。
是的我考虑过Dirty field方法,但正如我所说,这需要由系统级别来管理。如果对象被标记为脏其他对象
请注意,在.NET中有很多类可以做到这一点:许多IDisposable
类(特别是从Stream
继承的类!)当Dispose()
d时,它们设置了一个disposed
标记为true
,在属性/方法中标记为if (disposed) throw ObjectDisposedException()
。在您的情况下,您不应该这样做,您应该只是return;
或return (some default value)
;
public class ObjectWithReferences : IDisposable
{
private List<ObjectWithReferences> childs;
protected readonly ObjectWithReferences Parent;
public bool IsDisposed { get; private set; }
protected ObjectWithReferences(ObjectWithReferences parent)
{
Parent = parent;
if (parent != null)
{
parent.AddChild(this);
}
}
private void AddChild(ObjectWithReferences child)
{
if (IsDisposed)
{
child.Dispose();
return;
}
if (childs == null)
{
childs = new List<ObjectWithReferences>();
}
childs.Add(child);
}
private void DisposeChilds()
{
if (childs == null)
{
return;
}
foreach (ObjectWithReferences child in childs)
{
if (!child.IsDisposed)
{
child.Dispose();
}
}
childs = null;
}
public void Dispose()
{
if (!IsDisposed)
{
try
{
Dispose(true);
}
finally
{
try
{
DisposeChilds();
}
finally
{
IsDisposed = true;
GC.SuppressFinalize(this);
}
}
}
}
~ObjectWithReferences()
{
if (!IsDisposed)
{
try
{
Dispose(false);
}
finally
{
try
{
DisposeChilds();
}
finally
{
IsDisposed = true;
}
}
}
}
protected virtual void Dispose(bool disposing)
{
// Does nothing, not necessary to call!
}
}
使用示例:
public class ExampleRoot : ObjectWithReferences
{
public ExampleRoot() : base(null)
{
}
public void Foo()
{
if (IsDisposed)
{
return;
}
// Do Foo things
}
public void CreateChild()
{
if (IsDisposed)
{
return;
}
// Auto-adds itself!
var child = new ExampleChild(this);
}
}
public class ExampleChild : ObjectWithReferences
{
private byte[] BigBuffer = new byte[1000000];
public ExampleChild(ExampleRoot parent) : base(parent)
{
}
protected override void Dispose(bool disposing)
{
// The ExampleChild object has a very long possible lifetime,
// because it will live even in the IsDisposed == true state,
// so it is better to free even managed resources.
BigBuffer = null;
}
}
代码非常简单/清晰......有两个示例类(Root
和Child
)。基本思想是一个“特殊”对象ObjectWithReferences
,用于保存其子对象的引用。它是IDisposable
,当调用Dispose()
时(或最终确定时)Dispose()
所有子对象。您可以使用类继承此对象。您的方法/属性的每个人都应该始终检查IsDisposed
属性以查看对象是否已被处置。如果它已被处置,它们应该什么都不做并返回默认值(0,null
,string.Empty
,...)。请注意,如果其中一个对象保留对大型托管对象(例如数组)的引用,则与建议的.NET准则相反,应该null
这些引用让GC收集它们。
请注意,构造函数将正在构建的对象添加到其父对象中!
答案 1 :(得分:0)
这里正常的做法是使用WeakReference
。
如果您需要自动存根行为,您可以执行以下操作:
public class AutoStubbed<T> where T:class
{
private WeakReference<T> _reference;
private T _stub;
private readonly Func<T> _stubFactory;
public AutoStubbed(T value, T stub)
{
_reference = new WeakReference<T>(value);
_stub = stub;
}
public AutoStubbed(T value, Func<T> factory)
{
_reference = new WeakReference<T>(value);
_stubFactory = factory;
}
public T Target
{
get
{
T ret;
if(_reference.TryGetTarget(out ret))
return ret;
if(_stub == null && _stubFactory != null)
_stub = _stubFactory();
return _stub;
}
}
}
在对象和存根定义的接口中键入T
,而不是对象的类型。