C#(.NET)拥有对象的依赖生命周期的对象

时间:2015-05-28 11:58:53

标签: c# .net memory-management dependency-management

我来自C ++背景,并希望在下面的问题陈述中得到C#(.NET)专家的一些想法,我对解决方案方法持开放态度,但要求被冻结。

问题陈述:

  1. 让系统在删除拥有对象后立即自动清理从属对象(与GC提供的内容略有不同。)

  2. 从属对象可能具有除其拥有对象之外的其他引用,但只要删除拥有对象,依赖对象就需要进行

  3. 能够用存根对象(占位符)引用替换其他未完成的引用作为实际对象不再退出

  4. 系统需要与对象无关,并且应该能够检测引用或将其替换为从System.Object继承的任何对象(.net)

  5. 术语定义:

    依赖对象:始终需要所有者的对象,但也可以由其他对象引用。然而,依赖对象的生命周期将由拥有对象 完全 拥有。如果删除了拥有对象,则必须删除依赖对象。

    存根对象这些是表示已删除的引用的对象。

    功能背景

    为了能够支持功能需求,我们需要一个系统,它将自动清理所有者被删除的依赖对象,然后它将用存根替换其他引用,以指示它所持有的对象具有已被删除或卸载,

    用一个简单的例子解释这个

    1. 时间T1 - 假设我们创建了一个Line对象。由于创建一条线需要一个起点和终点,因此它创建了2个Point(Pt1和Pt2)对象。 Point对象标记为Dependent对象,Line Object是Owner。因此,在任何时候,如果我们删除Line,它应该去删除Pt1和Pt2。

    2. 时间T2:我们创建两个新点Pt3和Pt4(这些是现在的独立对象)

    3. 时间T3:我们创建一个参考的曲线对象(Pt2,Pt3和Pt4)。在这里,Pt2的生命周期由Line对象控制。

    4. 时间T4:我们从图形中删除Line对象,现在作为一项要求,此操作必须去除并删除Pt1和Pt2,因为它们是由Line和Line对象创建的。

    5. 时间T5:由于曲线也引用了Pt2,因此现在它的几何计算是不完整的,并且将被引用到存根对象。曲线对象将被标记为已损坏,以便在将来的时间点我们可以编辑它以引用新点。

    6. enter image description here

      拥有这个系统的关键问题是因为删除是由.NET系统控制的,所以我们无法控制它。任何想过如何在C#或.NET中实现这一点(在C ++中我们完全控制内存管理,因此可以在删除指针之前确定指针的活动引用,并在内存中删除或替换它们。)

      我理解垃圾收集器有其巨大的好处,但这也是我们需要在基于.NET的C#模型中支持的关键要求。

      任何想法,建议都表示赞赏。

2 个答案:

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

代码非常简单/清晰......有两个示例类(RootChild)。基本思想是一个“特殊”对象ObjectWithReferences,用于保存其子对象的引用。它是IDisposable,当调用Dispose()时(或最终确定时)Dispose()所有子对象。您可以使用类继承此对象。您的方法/属性的每个人都应该始终检查IsDisposed属性以查看对象是否已被处置。如果它已被处置,它们应该什么都不做并返回默认值(0,nullstring.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,而不是对象的类型。